[8.1] [Cases] Revert preconfigured connectors support for Cases (#130372) (#131043)

* [Cases] Revert preconfigured connectors support for Cases (#130372)

* Fix bug with deprecated connectors

* Add integration test

* Improve integration tests

* Fix grammar

* Fix CI

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
(cherry picked from commit 12fdfd63ad)

# Conflicts:
#	x-pack/plugins/cases/server/client/configure/client.test.ts

* Fix linting
This commit is contained in:
Christos Nasikas 2022-04-27 15:09:08 +03:00 committed by GitHub
parent 2e64711053
commit c96cd10a2f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 311 additions and 160 deletions

View file

@ -146,6 +146,25 @@ describe('ServiceNowITSM Fields', () => {
expect(screen.queryByTestId('deprecated-connector-warning-callout')).not.toBeInTheDocument();
});
it('does not show the deprecated callout when the connector is preconfigured', async () => {
render(
<Fields
fields={fields}
onChange={onChange}
connector={{ ...connector, isPreconfigured: true }}
/>
);
expect(screen.queryByTestId('deprecated-connector-warning-callout')).not.toBeInTheDocument();
});
it('does not show the deprecated callout when the config of the connector is undefined', async () => {
render(
// @ts-expect-error
<Fields fields={fields} onChange={onChange} connector={{ ...connector, config: undefined }} />
);
expect(screen.queryByTestId('deprecated-connector-warning-callout')).not.toBeInTheDocument();
});
it('should hide subcategory if selecting a category without subcategories', async () => {
// Failed Login doesn't have defined subcategories
const customFields = {

View file

@ -180,6 +180,25 @@ describe('ServiceNowSIR Fields', () => {
expect(screen.queryByTestId('deprecated-connector-warning-callout')).not.toBeInTheDocument();
});
it('does not show the deprecated callout when the connector is preconfigured', async () => {
render(
<Fields
fields={fields}
onChange={onChange}
connector={{ ...connector, isPreconfigured: true }}
/>
);
expect(screen.queryByTestId('deprecated-connector-warning-callout')).not.toBeInTheDocument();
});
it('does not show the deprecated callout when the config of the connector is undefined', async () => {
render(
// @ts-expect-error
<Fields fields={fields} onChange={onChange} connector={{ ...connector, config: undefined }} />
);
expect(screen.queryByTestId('deprecated-connector-warning-callout')).not.toBeInTheDocument();
});
test('it should hide subcategory if selecting a category without subcategories', async () => {
// Failed Login doesn't have defined subcategories
const customFields = {

View file

@ -22,7 +22,7 @@ describe('ServiceNow validator', () => {
expect(connectorValidator(invalidConnector)).toEqual({ message: 'Deprecated connector' });
});
test('it does not returns an error message if the connector does not uses the table API', () => {
test('it does not return an error message if the connector does not uses the table API', () => {
const invalidConnector = {
...connector,
config: {
@ -33,5 +33,16 @@ describe('ServiceNow validator', () => {
expect(connectorValidator(invalidConnector)).toBeFalsy();
});
test('it does not return an error message if the config of the connector is undefined', () => {
const { config, ...invalidConnector } = connector;
// @ts-expect-error
expect(connectorValidator(invalidConnector)).toBeFalsy();
});
test('it does not return an error message if the config of the connector is preconfigured', () => {
expect(connectorValidator({ ...connector, isPreconfigured: true })).toBeFalsy();
});
});
});

View file

@ -15,10 +15,18 @@ import { CaseActionConnector } from '../../types';
export const connectorValidator = (
connector: CaseActionConnector
): ReturnType<ValidationConfig['validator']> => {
const {
config: { usesTableApi },
} = connector;
if (usesTableApi) {
/**
* It is not possible to know if a preconfigured connector
* is deprecated or not as the config property of a
* preconfigured connector is not returned by the
* actions framework
*/
if (connector.isPreconfigured || connector.config == null) {
return;
}
if (connector.config?.usesTableApi) {
return {
message: 'Deprecated connector',
};

View file

@ -83,5 +83,16 @@ describe('Utils', () => {
})
).toBe(true);
});
it('returns false if the connector preconfigured', () => {
expect(isDeprecatedConnector({ ...connector, isPreconfigured: true })).toBe(false);
});
it('returns false if the config is undefined', () => {
expect(
// @ts-expect-error
isDeprecatedConnector({ ...connector, config: undefined })
).toBe(false);
});
});
});

View file

@ -74,7 +74,13 @@ export const getConnectorIcon = (
// TODO: Remove when the applications are certified
export const isDeprecatedConnector = (connector?: CaseActionConnector): boolean => {
if (connector == null) {
/**
* It is not possible to know if a preconfigured connector
* is deprecated or not as the config property of a
* preconfigured connector is not returned by the
* actions framework
*/
if (connector == null || connector.config == null || connector.isPreconfigured) {
return false;
}

View file

@ -9,7 +9,6 @@ import { CasesClientArgs } from '../types';
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
import { getConnectors } from './client';
import { actionsClientMock } from '../../../../actions/server/mocks';
import { ActionType } from '../../../../actions/common/types';
describe('client', () => {
describe('getConnectors', () => {
@ -18,66 +17,77 @@ describe('client', () => {
const args = { actionsClient, logger } as unknown as CasesClientArgs;
const jiraType: ActionType = {
id: '.jira',
name: '1',
enabled: true,
enabledInConfig: true,
enabledInLicense: true,
minimumLicenseRequired: 'basic',
};
const actionTypes = [
{
id: '.jira',
name: '1',
enabled: true,
enabledInConfig: true,
enabledInLicense: true,
minimumLicenseRequired: 'basic' as const,
},
{
id: '.servicenow',
name: '2',
enabled: true,
enabledInConfig: true,
enabledInLicense: true,
minimumLicenseRequired: 'basic' as const,
},
{
id: '.unsupported',
name: '3',
enabled: true,
enabledInConfig: true,
enabledInLicense: true,
minimumLicenseRequired: 'basic' as const,
},
{
id: '.swimlane',
name: 'swimlane',
enabled: true,
enabledInConfig: true,
enabledInLicense: false,
minimumLicenseRequired: 'basic' as const,
},
];
const connectors = [
{
id: '1',
actionTypeId: '.jira',
name: '1',
config: {},
isPreconfigured: false,
referencedByCount: 1,
},
{
id: '2',
actionTypeId: '.servicenow',
name: '2',
config: {},
isPreconfigured: false,
referencedByCount: 1,
},
{
id: '3',
actionTypeId: '.unsupported',
name: '3',
config: {},
isPreconfigured: false,
referencedByCount: 1,
},
];
beforeEach(() => {
jest.clearAllMocks();
});
it('removes connectors without a config field defined', async () => {
actionsClient.listTypes.mockImplementation(async () => [jiraType]);
it('remove unsupported connectors', async () => {
actionsClient.listTypes.mockImplementation(async () => actionTypes);
actionsClient.getAll.mockImplementation(async () => connectors);
actionsClient.getAll.mockImplementation(async () => [
{
id: '1',
actionTypeId: '.jira',
name: '1',
isPreconfigured: false,
referencedByCount: 1,
},
]);
expect(await getConnectors(args)).toEqual([]);
});
it('removes connectors that are pre configured', async () => {
actionsClient.listTypes.mockImplementation(async () => [jiraType]);
actionsClient.getAll.mockImplementation(async () => [
{
id: '1',
actionTypeId: '.jira',
name: '1',
config: {},
isPreconfigured: true,
referencedByCount: 1,
},
]);
expect(await getConnectors(args)).toEqual([]);
});
it('includes connectors that have a config and are not pre configured', async () => {
actionsClient.listTypes.mockImplementation(async () => [
jiraType,
{
id: '.servicenow',
name: '2',
enabled: true,
enabledInConfig: true,
enabledInLicense: true,
minimumLicenseRequired: 'basic',
},
]);
const connectors = [
expect(await getConnectors(args)).toEqual([
{
id: '1',
actionTypeId: '.jira',
@ -94,11 +104,83 @@ describe('client', () => {
isPreconfigured: false,
referencedByCount: 1,
},
];
]);
});
actionsClient.getAll.mockImplementation(async () => connectors);
it('returns preconfigured connectors', async () => {
actionsClient.listTypes.mockImplementation(async () => actionTypes);
actionsClient.getAll.mockImplementation(async () => [
...connectors,
{
id: '4',
actionTypeId: '.servicenow',
name: 'sn-preconfigured',
config: {},
isPreconfigured: true,
referencedByCount: 1,
},
]);
expect(await getConnectors(args)).toEqual(connectors);
expect(await getConnectors(args)).toEqual([
{
id: '1',
actionTypeId: '.jira',
name: '1',
config: {},
isPreconfigured: false,
referencedByCount: 1,
},
{
id: '2',
actionTypeId: '.servicenow',
name: '2',
config: {},
isPreconfigured: false,
referencedByCount: 1,
},
{
id: '4',
actionTypeId: '.servicenow',
name: 'sn-preconfigured',
config: {},
isPreconfigured: true,
referencedByCount: 1,
},
]);
});
it('filter out connectors that are unsupported by the current license', async () => {
actionsClient.listTypes.mockImplementation(async () => actionTypes);
actionsClient.getAll.mockImplementation(async () => [
...connectors,
{
id: '4',
actionTypeId: '.swimlane',
name: 'swimlane',
config: {},
isPreconfigured: false,
referencedByCount: 1,
},
]);
expect(await getConnectors(args)).toEqual([
{
id: '1',
actionTypeId: '.jira',
name: '1',
config: {},
isPreconfigured: false,
referencedByCount: 1,
},
{
id: '2',
actionTypeId: '.servicenow',
name: '2',
config: {},
isPreconfigured: false,
referencedByCount: 1,
},
]);
});
});
});

View file

@ -227,9 +227,7 @@ function isConnectorSupported(
): boolean {
return (
SUPPORTED_CONNECTORS.includes(action.actionTypeId) &&
actionTypes[action.actionTypeId]?.enabledInLicense &&
action.config != null &&
!action.isPreconfigured
actionTypes[action.actionTypeId]?.enabledInLicense
);
}

View file

@ -20,7 +20,6 @@ interface CreateTestConfigOptions {
testFiles?: string[];
}
// test.not-enabled is specifically not enabled
const enabledActionTypes = [
'.email',
'.index',
@ -139,6 +138,19 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions)
(pluginDir) =>
`--plugin-path=${path.resolve(__dirname, 'fixtures', 'plugins', pluginDir)}`
),
`--xpack.actions.preconfigured=${JSON.stringify({
'preconfigured-servicenow': {
name: 'preconfigured-servicenow',
actionTypeId: '.servicenow',
config: {
apiUrl: 'https://example.com',
},
secrets: {
username: 'elastic',
password: 'elastic',
},
},
})}`,
`--server.xsrf.allowlist=${JSON.stringify(getAllExternalServiceSimulatorPaths())}`,
...(ssl
? [

View file

@ -286,6 +286,19 @@ export const getWebhookConnector = () => ({
},
});
export const getEmailConnector = () => ({
name: 'An email action',
connector_type_id: '.email',
config: {
service: '__json',
from: 'bob@example.com',
},
secrets: {
user: 'bob',
password: 'supersecret',
},
});
interface CommonSavedObjectAttributes {
id?: string | null;
created_at?: string | null;

View file

@ -1,20 +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 { createConnector, getServiceNowConnector } from '../../../../common/lib/utils';
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
// eslint-disable-next-line import/no-default-export
export default function serviceNow({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
describe('create service now action', () => {
it('should return 403 when creating a service now action', async () => {
await createConnector({ supertest, req: getServiceNowConnector(), expectedHttpCode: 403 });
});
});
}

View file

@ -15,13 +15,18 @@ export default ({ getService }: FtrProviderContext): void => {
const supertest = getService('supertest');
describe('get_connectors', () => {
it('should return an empty find body correctly if no connectors are loaded', async () => {
/**
* A ServiceNow preconfigured connector is registered here
* x-pack/test/cases_api_integration/common/config.ts
*
* The license for this test is set to basic. ServiceNow connectors
* needs license >= platinum. The test below ensures
* that connectors without valid license are being filtered correctly
*/
it('should return an empty list of connectors', async () => {
const connectors = await getCaseConnectors({ supertest });
expect(connectors).to.eql([]);
});
it.skip('filters out connectors that are not enabled in license', async () => {
// TODO: Should find a way to downgrade license to gold and upgrade back to trial
expect(connectors).to.eql([]);
});
});
};

View file

@ -24,6 +24,7 @@ export default ({ loadTestFile, getService }: FtrProviderContext): void => {
// Basic
loadTestFile(require.resolve('./cases/push_case'));
loadTestFile(require.resolve('./configure/get_connectors'));
// Common
loadTestFile(require.resolve('../common'));

View file

@ -31,7 +31,6 @@ export default ({ loadTestFile }: FtrProviderContext): void => {
loadTestFile(require.resolve('./cases/tags/get_tags'));
loadTestFile(require.resolve('./user_actions/get_all_user_actions'));
loadTestFile(require.resolve('./configure/get_configure'));
loadTestFile(require.resolve('./configure/get_connectors'));
loadTestFile(require.resolve('./configure/patch_configure'));
loadTestFile(require.resolve('./configure/post_configure'));
loadTestFile(require.resolve('./metrics/get_case_metrics'));

View file

@ -8,7 +8,6 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../../../common/ftr_provider_context';
import { CASE_CONFIGURE_CONNECTORS_URL } from '../../../../../../plugins/cases/common/constants';
import { ObjectRemover as ActionsRemover } from '../../../../../alerting_api_integration/common/lib';
import {
getServiceNowConnector,
@ -16,6 +15,8 @@ import {
getResilientConnector,
createConnector,
getServiceNowSIRConnector,
getEmailConnector,
getCaseConnectors,
} from '../../../../common/lib/utils';
// eslint-disable-next-line import/no-default-export
@ -29,41 +30,10 @@ export default ({ getService }: FtrProviderContext): void => {
});
it('should return the correct connectors', async () => {
const { body: snConnector } = await supertest
.post('/api/actions/connector')
.set('kbn-xsrf', 'true')
.send(getServiceNowConnector())
.expect(200);
const { body: emailConnector } = await supertest
.post('/api/actions/connector')
.set('kbn-xsrf', 'true')
.send({
name: 'An email action',
connector_type_id: '.email',
config: {
service: '__json',
from: 'bob@example.com',
},
secrets: {
user: 'bob',
password: 'supersecret',
},
})
.expect(200);
const { body: jiraConnector } = await supertest
.post('/api/actions/connector')
.set('kbn-xsrf', 'true')
.send(getJiraConnector())
.expect(200);
const { body: resilientConnector } = await supertest
.post('/api/actions/connector')
.set('kbn-xsrf', 'true')
.send(getResilientConnector())
.expect(200);
const snConnector = await createConnector({ supertest, req: getServiceNowConnector() });
const emailConnector = await createConnector({ supertest, req: getEmailConnector() });
const jiraConnector = await createConnector({ supertest, req: getJiraConnector() });
const resilientConnector = await createConnector({ supertest, req: getResilientConnector() });
const sir = await createConnector({ supertest, req: getServiceNowSIRConnector() });
actionsRemover.add('default', sir.id, 'action', 'actions');
@ -72,11 +42,7 @@ export default ({ getService }: FtrProviderContext): void => {
actionsRemover.add('default', jiraConnector.id, 'action', 'actions');
actionsRemover.add('default', resilientConnector.id, 'action', 'actions');
const { body: connectors } = await supertest
.get(`${CASE_CONFIGURE_CONNECTORS_URL}/_find`)
.set('kbn-xsrf', 'true')
.send()
.expect(200);
const connectors = await getCaseConnectors({ supertest });
expect(connectors).to.eql([
{
@ -91,6 +57,17 @@ export default ({ getService }: FtrProviderContext): void => {
isMissingSecrets: false,
referencedByCount: 0,
},
/**
* Preconfigured connectors are being registered here:
* x-pack/test/cases_api_integration/common/config.ts
*/
{
actionTypeId: '.servicenow',
id: 'preconfigured-servicenow',
isPreconfigured: true,
name: 'preconfigured-servicenow',
referencedByCount: 0,
},
{
id: resilientConnector.id,
actionTypeId: '.resilient',

View file

@ -18,6 +18,7 @@ import {
getAuthWithSuperUser,
getCaseConnectors,
getActionsSpace,
getEmailConnector,
} from '../../../../common/lib/utils';
// eslint-disable-next-line import/no-default-export
@ -38,32 +39,25 @@ export default ({ getService }: FtrProviderContext): void => {
req: getServiceNowConnector(),
auth: authSpace1,
});
const emailConnector = await createConnector({
supertest,
req: {
name: 'An email action',
connector_type_id: '.email',
config: {
service: '__json',
from: 'bob@example.com',
},
secrets: {
user: 'bob',
password: 'supersecret',
},
},
req: getEmailConnector(),
auth: authSpace1,
});
const jiraConnector = await createConnector({
supertest,
req: getJiraConnector(),
auth: authSpace1,
});
const resilientConnector = await createConnector({
supertest,
req: getResilientConnector(),
auth: authSpace1,
});
const sir = await createConnector({
supertest,
req: getServiceNowSIRConnector(),
@ -91,6 +85,17 @@ export default ({ getService }: FtrProviderContext): void => {
isMissingSecrets: false,
referencedByCount: 0,
},
/**
* Preconfigured connectors are being registered here:
* x-pack/test/cases_api_integration/common/config.ts
*/
{
actionTypeId: '.servicenow',
id: 'preconfigured-servicenow',
isPreconfigured: true,
name: 'preconfigured-servicenow',
referencedByCount: 0,
},
{
id: resilientConnector.id,
actionTypeId: '.resilient',
@ -136,32 +141,25 @@ export default ({ getService }: FtrProviderContext): void => {
req: getServiceNowConnector(),
auth: authSpace1,
});
const emailConnector = await createConnector({
supertest,
req: {
name: 'An email action',
connector_type_id: '.email',
config: {
service: '__json',
from: 'bob@example.com',
},
secrets: {
user: 'bob',
password: 'supersecret',
},
},
req: getEmailConnector(),
auth: authSpace1,
});
const jiraConnector = await createConnector({
supertest,
req: getJiraConnector(),
auth: authSpace1,
});
const resilientConnector = await createConnector({
supertest,
req: getResilientConnector(),
auth: authSpace1,
});
const sir = await createConnector({
supertest,
req: getServiceNowSIRConnector(),
@ -179,7 +177,19 @@ export default ({ getService }: FtrProviderContext): void => {
auth: getAuthWithSuperUser('space2'),
});
expect(connectors).to.eql([]);
expect(connectors).to.eql([
/**
* Preconfigured connectors are being registered here:
* x-pack/test/cases_api_integration/common/config.ts
*/
{
actionTypeId: '.servicenow',
id: 'preconfigured-servicenow',
isPreconfigured: true,
name: 'preconfigured-servicenow',
referencedByCount: 0,
},
]);
});
});
};