mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Cases] Handle lens actions in Serverless (#163581)
## Summary fixes https://github.com/elastic/kibana/issues/160126 This PR - hides owner selection while adding lens to case from dashboard for observability and securitySolution serverless - hides add to new case and add to existing case lens action for Elasticsearch serverless **How to test** - Run Observability/Securtiy solution serverless project and add lens visualization to case from dashboard - Run ES serverless project and check that lens do not have option to add to case - Classic kibana works as before (check dashboard from securitySolution and generic dashboard as well) ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
e0c3c525ed
commit
ebfb8322e5
12 changed files with 316 additions and 7 deletions
|
@ -24,18 +24,18 @@ import { useCaseConfigureResponse } from '../configure_cases/__mock__';
|
|||
import { TestProviders } from '../../common/mock';
|
||||
import { useGetSupportedActionConnectors } from '../../containers/configure/use_get_supported_action_connectors';
|
||||
import { useGetTags } from '../../containers/use_get_tags';
|
||||
import { useAvailableCasesOwners } from '../app/use_available_owners';
|
||||
|
||||
jest.mock('../../containers/use_get_tags');
|
||||
jest.mock('../../containers/configure/use_get_supported_action_connectors');
|
||||
jest.mock('../../containers/configure/use_configure');
|
||||
jest.mock('../markdown_editor/plugins/lens/use_lens_draft_comment');
|
||||
jest.mock('../app/use_available_owners', () => ({
|
||||
useAvailableCasesOwners: () => ['securitySolution', 'observability'],
|
||||
}));
|
||||
jest.mock('../app/use_available_owners');
|
||||
|
||||
const useGetTagsMock = useGetTags as jest.Mock;
|
||||
const useGetConnectorsMock = useGetSupportedActionConnectors as jest.Mock;
|
||||
const useCaseConfigureMock = useCaseConfigure as jest.Mock;
|
||||
const useAvailableOwnersMock = useAvailableCasesOwners as jest.Mock;
|
||||
|
||||
const initialCaseValue: FormProps = {
|
||||
description: '',
|
||||
|
@ -77,6 +77,7 @@ describe('CreateCaseForm', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
useAvailableOwnersMock.mockReturnValue(['securitySolution', 'observability']);
|
||||
useGetTagsMock.mockReturnValue({ data: ['test'] });
|
||||
useGetConnectorsMock.mockReturnValue({ isLoading: false, data: connectorsMock });
|
||||
useCaseConfigureMock.mockImplementation(() => useCaseConfigureResponse);
|
||||
|
@ -138,6 +139,18 @@ describe('CreateCaseForm', () => {
|
|||
expect(wrapper.find(`[data-test-subj="caseOwnerSelector"]`).exists()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('does not render solution picker when only one owner is available', async () => {
|
||||
useAvailableOwnersMock.mockReturnValue(['securitySolution']);
|
||||
|
||||
const wrapper = mount(
|
||||
<MockHookWrapperComponent>
|
||||
<CreateCaseForm {...casesFormProps} />
|
||||
</MockHookWrapperComponent>
|
||||
);
|
||||
|
||||
expect(wrapper.find(`[data-test-subj="caseOwnerSelector"]`).exists()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('hides the sync alerts toggle', () => {
|
||||
const { queryByText } = render(
|
||||
<MockHookWrapperComponent testProviderProps={{ features: { alerts: { sync: false } } }}>
|
||||
|
|
|
@ -86,9 +86,8 @@ export const CreateCaseFormFields: React.FC<CreateCaseFormFieldsProps> = React.m
|
|||
({ connectors, isLoadingConnectors, withSteps, owner, draftStorageKey }) => {
|
||||
const { isSubmitting } = useFormContext();
|
||||
const { isSyncAlertsEnabled, caseAssignmentAuthorized } = useCasesFeatures();
|
||||
|
||||
const availableOwners = useAvailableCasesOwners();
|
||||
const canShowCaseSolutionSelection = !owner.length && availableOwners.length;
|
||||
const canShowCaseSolutionSelection = !owner.length && availableOwners.length > 1;
|
||||
|
||||
const firstStep = useMemo(
|
||||
() => ({
|
||||
|
|
|
@ -26,6 +26,7 @@ import {
|
|||
getConnectorsFormDeserializer,
|
||||
getConnectorsFormSerializer,
|
||||
} from '../utils';
|
||||
import { useAvailableCasesOwners } from '../app/use_available_owners';
|
||||
import type { CaseAttachmentsWithoutOwner } from '../../types';
|
||||
import { useGetSupportedActionConnectors } from '../../containers/configure/use_get_supported_action_connectors';
|
||||
import { useCreateCaseWithAttachmentsTransaction } from '../../common/apm/use_cases_transactions';
|
||||
|
@ -68,6 +69,7 @@ export const FormContext: React.FC<Props> = ({
|
|||
const { mutateAsync: createAttachments } = useCreateAttachments();
|
||||
const { mutateAsync: pushCaseToExternalService } = usePostPushToService();
|
||||
const { startTransaction } = useCreateCaseWithAttachmentsTransaction();
|
||||
const availableOwners = useAvailableCasesOwners();
|
||||
|
||||
const submitCase = useCallback(
|
||||
async (
|
||||
|
@ -82,6 +84,7 @@ export const FormContext: React.FC<Props> = ({
|
|||
if (isValid) {
|
||||
const { selectedOwner, ...userFormData } = dataWithoutConnectorId;
|
||||
const caseConnector = getConnectorById(dataConnectorId, connectors);
|
||||
const defaultOwner = owner[0] ?? availableOwners[0];
|
||||
|
||||
startTransaction({ appId, attachments });
|
||||
|
||||
|
@ -94,7 +97,7 @@ export const FormContext: React.FC<Props> = ({
|
|||
...userFormData,
|
||||
connector: connectorToUpdate,
|
||||
settings: { syncAlerts },
|
||||
owner: selectedOwner ?? owner[0],
|
||||
owner: selectedOwner ?? defaultOwner,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -131,6 +134,7 @@ export const FormContext: React.FC<Props> = ({
|
|||
attachments,
|
||||
postCase,
|
||||
owner,
|
||||
availableOwners,
|
||||
afterCaseCreated,
|
||||
onSuccess,
|
||||
createAttachments,
|
||||
|
|
|
@ -27,6 +27,7 @@ export const createCase = async (
|
|||
|
||||
const { body: theCase } = await apiCall
|
||||
.set('kbn-xsrf', 'true')
|
||||
.set('x-elastic-internal-origin', 'foo')
|
||||
.set(headers)
|
||||
.send(params)
|
||||
.expect(expectedHttpCode);
|
||||
|
|
|
@ -26,7 +26,6 @@ export function createTestConfig(options: CreateTestConfigOptions) {
|
|||
...svlSharedConfig.get('kbnTestServer.serverArgs'),
|
||||
`--serverless=${options.serverlessProject}`,
|
||||
`--xpack.alerting.enableFrameworkAlerts=true`,
|
||||
'--xpack.encryptedSavedObjects.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"',
|
||||
'--xpack.observability.unsafe.thresholdRule.enabled=true',
|
||||
'--server.publicBaseUrl=https://localhost:5601',
|
||||
],
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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 { expect } from 'expect';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default ({ getPageObject, getService }: FtrProviderContext) => {
|
||||
const dashboard = getPageObject('dashboard');
|
||||
const lens = getPageObject('lens');
|
||||
const svlCommonNavigation = getPageObject('svlCommonNavigation');
|
||||
const svlObltNavigation = getService('svlObltNavigation');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const cases = getService('cases');
|
||||
const find = getService('find');
|
||||
|
||||
describe('persistable attachment', () => {
|
||||
describe('lens visualization', () => {
|
||||
before(async () => {
|
||||
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional');
|
||||
await kibanaServer.importExport.load(
|
||||
'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json'
|
||||
);
|
||||
|
||||
await svlObltNavigation.navigateToLandingPage();
|
||||
|
||||
await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'dashboards' });
|
||||
|
||||
await dashboard.clickNewDashboard();
|
||||
|
||||
await lens.createAndAddLensFromDashboard({});
|
||||
|
||||
await dashboard.waitForRenderComplete();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await cases.api.deleteAllCases();
|
||||
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/logstash_functional');
|
||||
await kibanaServer.importExport.unload(
|
||||
'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json'
|
||||
);
|
||||
});
|
||||
|
||||
it('adds lens visualization to a new case', async () => {
|
||||
const caseTitle = 'case created in observability from my dashboard with lens visualization';
|
||||
|
||||
await testSubjects.click('embeddablePanelToggleMenuIcon');
|
||||
await testSubjects.click('embeddablePanelMore-mainMenu');
|
||||
await testSubjects.click('embeddablePanelAction-embeddable_addToNewCase');
|
||||
|
||||
await testSubjects.existOrFail('create-case-flyout');
|
||||
|
||||
await testSubjects.setValue('input', caseTitle);
|
||||
|
||||
await testSubjects.setValue('euiMarkdownEditorTextArea', 'test description');
|
||||
|
||||
// verify that solution picker is not visible
|
||||
await testSubjects.missingOrFail('caseOwnerSelector');
|
||||
|
||||
await testSubjects.click('create-case-submit');
|
||||
|
||||
await cases.common.expectToasterToContain(`${caseTitle} has been updated`);
|
||||
|
||||
await testSubjects.click('toaster-content-case-view-link');
|
||||
|
||||
if (await testSubjects.exists('appLeaveConfirmModal')) {
|
||||
await testSubjects.exists('confirmModalConfirmButton');
|
||||
await testSubjects.click('confirmModalConfirmButton');
|
||||
}
|
||||
|
||||
const title = await find.byCssSelector('[data-test-subj="editable-title-header-value"]');
|
||||
expect(await title.getVisibleText()).toEqual(caseTitle);
|
||||
|
||||
await testSubjects.existOrFail('comment-persistableState-.lens');
|
||||
});
|
||||
|
||||
it('adds lens visualization to an existing case from dashboard', async () => {
|
||||
const theCaseTitle = 'case already exists in observability!!';
|
||||
const theCase = await cases.api.createCase({
|
||||
title: theCaseTitle,
|
||||
description: 'This is a test case to verify existing action scenario!!',
|
||||
owner: 'observability',
|
||||
});
|
||||
|
||||
await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'dashboards' });
|
||||
|
||||
await testSubjects.click('embeddablePanelToggleMenuIcon');
|
||||
await testSubjects.click('embeddablePanelMore-mainMenu');
|
||||
await testSubjects.click('embeddablePanelAction-embeddable_addToExistingCase');
|
||||
|
||||
// verify that solution filter is not visible
|
||||
await testSubjects.missingOrFail('solution-filter-popover-button');
|
||||
|
||||
await testSubjects.click(`cases-table-row-select-${theCase.id}`);
|
||||
|
||||
await cases.common.expectToasterToContain(`${theCaseTitle} has been updated`);
|
||||
await testSubjects.click('toaster-content-case-view-link');
|
||||
|
||||
if (await testSubjects.exists('appLeaveConfirmModal')) {
|
||||
await testSubjects.exists('confirmModalConfirmButton');
|
||||
await testSubjects.click('confirmModalConfirmButton');
|
||||
}
|
||||
|
||||
const title = await find.byCssSelector('[data-test-subj="editable-title-header-value"]');
|
||||
expect(await title.getVisibleText()).toEqual(theCaseTitle);
|
||||
|
||||
await testSubjects.existOrFail('comment-persistableState-.lens');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
|
@ -13,5 +13,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
|
|||
loadTestFile(require.resolve('./landing_page'));
|
||||
loadTestFile(require.resolve('./navigation'));
|
||||
loadDiscoverLogExplorerSuite(loadTestFile);
|
||||
loadTestFile(require.resolve('./cases/attachment_framework'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default ({ getPageObject, getService }: FtrProviderContext) => {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const dashboard = getPageObject('dashboard');
|
||||
const lens = getPageObject('lens');
|
||||
const svlSearchNavigation = getService('svlSearchNavigation');
|
||||
const svlCommonNavigation = getPageObject('svlCommonNavigation');
|
||||
|
||||
describe('persistable attachment', () => {
|
||||
describe('lens visualization', () => {
|
||||
before(async () => {
|
||||
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional');
|
||||
await kibanaServer.importExport.load(
|
||||
'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json'
|
||||
);
|
||||
|
||||
await svlSearchNavigation.navigateToLandingPage();
|
||||
|
||||
await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'dashboards' });
|
||||
|
||||
await dashboard.clickNewDashboard();
|
||||
|
||||
await lens.createAndAddLensFromDashboard({});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/logstash_functional');
|
||||
await kibanaServer.importExport.unload(
|
||||
'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json'
|
||||
);
|
||||
});
|
||||
|
||||
it('does not show actions to add lens visualization to case', async () => {
|
||||
await testSubjects.click('embeddablePanelToggleMenuIcon');
|
||||
await testSubjects.click('embeddablePanelMore-mainMenu');
|
||||
await testSubjects.missingOrFail('embeddablePanelAction-embeddable_addToNewCase');
|
||||
await testSubjects.missingOrFail('embeddablePanelAction-embeddable_addToExistingCase');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
|
@ -11,5 +11,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
|
|||
describe('serverless search UI', function () {
|
||||
loadTestFile(require.resolve('./landing_page'));
|
||||
loadTestFile(require.resolve('./navigation'));
|
||||
loadTestFile(require.resolve('./cases/attachment_framework'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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 { expect } from 'expect';
|
||||
import { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||
|
||||
export default ({ getPageObject, getService }: FtrProviderContext) => {
|
||||
const dashboard = getPageObject('dashboard');
|
||||
const lens = getPageObject('lens');
|
||||
const svlSecNavigation = getService('svlSecNavigation');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const cases = getService('cases');
|
||||
const find = getService('find');
|
||||
|
||||
describe('persistable attachment', () => {
|
||||
describe('lens visualization', () => {
|
||||
before(async () => {
|
||||
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional');
|
||||
await kibanaServer.importExport.load(
|
||||
'x-pack/test/functional/fixtures/kbn_archiver/dashboard/feature_controls/security/security.json'
|
||||
);
|
||||
|
||||
await svlSecNavigation.navigateToLandingPage();
|
||||
|
||||
await testSubjects.click('solutionSideNavItemLink-dashboards');
|
||||
|
||||
await dashboard.clickNewDashboard();
|
||||
|
||||
await lens.createAndAddLensFromDashboard({});
|
||||
|
||||
await dashboard.waitForRenderComplete();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await cases.api.deleteAllCases();
|
||||
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/logstash_functional');
|
||||
await kibanaServer.importExport.unload(
|
||||
'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json'
|
||||
);
|
||||
});
|
||||
|
||||
it('adds lens visualization to a new case', async () => {
|
||||
const caseTitle =
|
||||
'case created in security solution from my dashboard with lens visualization';
|
||||
|
||||
await testSubjects.click('embeddablePanelToggleMenuIcon');
|
||||
await testSubjects.click('embeddablePanelMore-mainMenu');
|
||||
await testSubjects.click('embeddablePanelAction-embeddable_addToNewCase');
|
||||
|
||||
await testSubjects.existOrFail('create-case-flyout');
|
||||
|
||||
await testSubjects.setValue('input', caseTitle);
|
||||
|
||||
await testSubjects.setValue('euiMarkdownEditorTextArea', 'test description');
|
||||
|
||||
// verify that solution picker is not visible
|
||||
await testSubjects.missingOrFail('caseOwnerSelector');
|
||||
|
||||
await testSubjects.click('create-case-submit');
|
||||
|
||||
await cases.common.expectToasterToContain(`${caseTitle} has been updated`);
|
||||
|
||||
await testSubjects.click('toaster-content-case-view-link');
|
||||
|
||||
if (await testSubjects.exists('appLeaveConfirmModal')) {
|
||||
await testSubjects.exists('confirmModalConfirmButton');
|
||||
await testSubjects.click('confirmModalConfirmButton');
|
||||
}
|
||||
|
||||
const title = await find.byCssSelector('[data-test-subj="editable-title-header-value"]');
|
||||
expect(await title.getVisibleText()).toEqual(caseTitle);
|
||||
|
||||
await testSubjects.existOrFail('comment-persistableState-.lens');
|
||||
});
|
||||
|
||||
it('adds lens visualization to an existing case from dashboard', async () => {
|
||||
const theCaseTitle = 'case already exists in security solution!!';
|
||||
const theCase = await cases.api.createCase({
|
||||
title: theCaseTitle,
|
||||
description: 'This is a test case to verify existing action scenario!!',
|
||||
owner: 'securitySolution',
|
||||
});
|
||||
|
||||
await testSubjects.click('solutionSideNavItemLink-dashboards');
|
||||
|
||||
if (await testSubjects.exists('edit-unsaved-New-Dashboard')) {
|
||||
await testSubjects.click('edit-unsaved-New-Dashboard');
|
||||
}
|
||||
|
||||
await testSubjects.click('embeddablePanelToggleMenuIcon');
|
||||
await testSubjects.click('embeddablePanelMore-mainMenu');
|
||||
await testSubjects.click('embeddablePanelAction-embeddable_addToExistingCase');
|
||||
|
||||
// verify that solution filter is not visible
|
||||
await testSubjects.missingOrFail('solution-filter-popover-button');
|
||||
|
||||
await testSubjects.click(`cases-table-row-select-${theCase.id}`);
|
||||
|
||||
await cases.common.expectToasterToContain(`${theCaseTitle} has been updated`);
|
||||
await testSubjects.click('toaster-content-case-view-link');
|
||||
|
||||
if (await testSubjects.exists('appLeaveConfirmModal')) {
|
||||
await testSubjects.exists('confirmModalConfirmButton');
|
||||
await testSubjects.click('confirmModalConfirmButton');
|
||||
}
|
||||
|
||||
const title = await find.byCssSelector('[data-test-subj="editable-title-header-value"]');
|
||||
expect(await title.getVisibleText()).toEqual(theCaseTitle);
|
||||
|
||||
await testSubjects.existOrFail('comment-persistableState-.lens');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
|
@ -12,5 +12,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
|
|||
loadTestFile(require.resolve('./ftr/landing_page'));
|
||||
loadTestFile(require.resolve('./ftr/navigation'));
|
||||
loadTestFile(require.resolve('./ftr/management'));
|
||||
loadTestFile(require.resolve('./ftr/cases/attachment_framework'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ export default async () => {
|
|||
appenders: ['deprecation'],
|
||||
},
|
||||
])}`,
|
||||
'--xpack.encryptedSavedObjects.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"',
|
||||
],
|
||||
},
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue