From 8bd7b3d4bb7658ec8d4553b7afa9b1863fc17263 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Thu, 6 Apr 2023 16:11:13 +0100 Subject: [PATCH] fix custom chart actions --- .../matrix_histogram/index.test.tsx | 52 ++++++++++++++----- .../components/matrix_histogram/index.tsx | 3 +- .../__mocks__/actions.tsx | 5 +- .../visualization_actions/actions.test.tsx | 42 ++++++++++----- .../visualization_actions/actions.tsx | 14 +++-- .../dns_top_domains.test.ts.snap | 4 +- .../network/dns_top_domains.ts | 9 ++-- .../components/visualization_actions/types.ts | 3 ++ 8 files changed, 97 insertions(+), 35 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx index 291ebdcd3ac4c..4a1ed7c791350 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx @@ -14,11 +14,12 @@ import { useMatrixHistogramCombined } from '../../containers/matrix_histogram'; import { MatrixHistogramType } from '../../../../common/search_strategy/security_solution'; import { TestProviders } from '../../mock'; import { mockRuntimeMappings } from '../../containers/source/mock'; -import { dnsTopDomainsLensAttributes } from '../visualization_actions/lens_attributes/network/dns_top_domains'; +import { getDnsTopDomainsLensAttributes } from '../visualization_actions/lens_attributes/network/dns_top_domains'; import { useQueryToggle } from '../../containers/query_toggle'; import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features'; import type { ExperimentalFeatures } from '../../../../common/experimental_features'; import { allowedExperimentalValues } from '../../../../common/experimental_features'; +import { VisualizationActions } from '../visualization_actions/actions'; jest.mock('../../containers/query_toggle'); @@ -61,19 +62,23 @@ describe('Matrix Histogram Component', () => { const mockMatrixOverTimeHistogramProps = { defaultIndex: ['defaultIndex'], - defaultStackByOption: { text: 'text', value: 'value' }, + defaultStackByOption: { + text: 'dns.question.registered_domain', + value: 'dns.question.registered_domain', + }, endDate: '2019-07-18T20:00:00.000Z', errorMessage: 'error', histogramType: MatrixHistogramType.alerts, id: 'mockId', indexNames: [], isInspected: false, - isPtrIncluded: false, + isPtrIncluded: true, setQuery: jest.fn(), skip: false, sourceId: 'default', - stackByField: 'mockStackByField', - stackByOptions: [{ text: 'text', value: 'value' }], + stackByOptions: [ + { text: 'dns.question.registered_domain', value: 'dns.question.registered_domain' }, + ], startDate: '2019-07-18T19:00: 00.000Z', subtitle: 'mockSubtitle', totalCount: -1, @@ -192,7 +197,7 @@ describe('Matrix Histogram Component', () => { test("it doesn't render Inspect button by default", () => { const testProps = { ...mockMatrixOverTimeHistogramProps, - lensAttributes: dnsTopDomainsLensAttributes, + getLensAttributes: getDnsTopDomainsLensAttributes, }; wrapper = mount(, { wrappingComponent: TestProviders, @@ -202,25 +207,48 @@ describe('Matrix Histogram Component', () => { }); describe('VisualizationActions', () => { - test('it renders VisualizationActions if lensAttributes is provided', () => { - const testProps = { - ...mockMatrixOverTimeHistogramProps, - lensAttributes: dnsTopDomainsLensAttributes, - }; + const testProps = { + ...mockMatrixOverTimeHistogramProps, + getLensAttributes: jest.fn().mockReturnValue(getDnsTopDomainsLensAttributes()), + }; + beforeEach(() => { wrapper = mount(, { wrappingComponent: TestProviders, }); + }); + test('it renders VisualizationActions if getLensAttributes is provided', () => { expect(wrapper.find('[data-test-subj="visualizationActions"]').exists()).toBe(true); expect(wrapper.find('[data-test-subj="visualizationActions"]').prop('className')).toEqual( 'histogram-viz-actions' ); }); + + test('it VisualizationActions with correct properties', () => { + expect((VisualizationActions as unknown as jest.Mock).mock.calls[0][0]).toEqual( + expect.objectContaining({ + className: 'histogram-viz-actions', + extraOptions: { + dnsIsPtrIncluded: testProps.isPtrIncluded, + }, + getLensAttributes: testProps.getLensAttributes, + lensAttributes: undefined, + isInspectButtonDisabled: true, + queryId: testProps.id, + stackByField: testProps.defaultStackByOption.value, + timerange: { + from: testProps.startDate, + to: testProps.endDate, + }, + title: testProps.title, + }) + ); + }); }); describe('toggle query', () => { const testProps = { ...mockMatrixOverTimeHistogramProps, - lensAttributes: dnsTopDomainsLensAttributes, + getLensAttributes: getDnsTopDomainsLensAttributes, }; test('toggleQuery updates toggleStatus', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx index d8e75ceafb5e3..82ef731321cb7 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx @@ -280,6 +280,7 @@ export const MatrixHistogramComponent: React.FC = = isChartEmbeddablesEnabled ? ( = lensAttributes={lensAttributes} stackByField={selectedStackByOption.value} timerange={timerange} - extraOptions={extraVisualizationOptions} /> ) : ( { +export const VisualizationActions = jest.fn((props: VisualizationActionsProps) => { const { title, className } = props; + return (
{title}
); -}; +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/actions.test.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/actions.test.tsx index 924f3ea80dcca..aa168343cdb90 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/actions.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/actions.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import type { Action } from '@kbn/ui-actions-plugin/public'; -import { dnsTopDomainsLensAttributes } from './lens_attributes/network/dns_top_domains'; +import { getDnsTopDomainsLensAttributes } from './lens_attributes/network/dns_top_domains'; import { VisualizationActions } from './actions'; import { createSecuritySolutionStorageMock, @@ -27,6 +27,9 @@ import { CASES_FEATURE_ID } from '../../../../common/constants'; import { mockCasesContract } from '@kbn/cases-plugin/public/mocks'; import { allCasesCapabilities, allCasesPermissions } from '../../../cases_test_utils'; import { InputsModelId } from '../../store/inputs/constants'; +import type { VisualizationActionsProps } from './types'; +import * as useLensAttributesModule from './use_lens_attributes'; +import { SourcererScopeName } from '../../store/sourcerer/model'; jest.mock('react-router-dom', () => { const actual = jest.requireActual('react-router-dom'); @@ -43,6 +46,7 @@ jest.mock('../../utils/route/use_route_spy', () => { useRouteSpy: jest.fn(() => [{ pageName: 'network', detailName: '', tabName: 'dns' }]), }; }); + describe('VisualizationActions', () => { const refetch = jest.fn(); const state: State = mockGlobalState; @@ -58,16 +62,19 @@ describe('VisualizationActions', () => { refetch, state: state.inputs, }; + const spyUseLensAttributes = jest.spyOn(useLensAttributesModule, 'useLensAttributes'); let store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - const props = { - lensAttributes: dnsTopDomainsLensAttributes, + const props: VisualizationActionsProps = { + getLensAttributes: getDnsTopDomainsLensAttributes, queryId: 'networkDnsHistogramQuery', timerange: { from: '2022-03-06T16:00:00.000Z', to: '2022-03-07T15:59:59.999Z', }, title: 'mock networkDnsHistogram', + extraOptions: { dnsIsPtrIncluded: true }, + stackByField: 'dns.question.registered_domain', }; const mockNavigateToPrefilledEditor = jest.fn(); const mockGetCreateCaseFlyoutOpen = jest.fn(); @@ -123,6 +130,25 @@ describe('VisualizationActions', () => { store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); }); + test('Should generate attributes', () => { + render( + + + + ); + expect(spyUseLensAttributes.mock.calls[0][0]).toEqual( + expect.objectContaining({ + applyGlobalQueriesAndFilters: true, + extraOptions: props.extraOptions, + getLensAttributes: props.getLensAttributes, + lensAttributes: props.lensAttributes, + scopeId: SourcererScopeName.default, + stackByField: props.stackByField, + title: '', + }) + ); + }); + test('Should render VisualizationActions button', () => { const { container } = render( @@ -156,21 +182,13 @@ describe('VisualizationActions', () => { fireEvent.click(screen.getByText('Open in Lens')); expect(mockNavigateToPrefilledEditor.mock.calls[0][0].timeRange).toEqual(props.timerange); - expect(mockNavigateToPrefilledEditor.mock.calls[0][0].attributes.title).toEqual( - props.lensAttributes.title - ); + expect(mockNavigateToPrefilledEditor.mock.calls[0][0].attributes.title).toEqual(''); expect(mockNavigateToPrefilledEditor.mock.calls[0][0].attributes.references).toEqual([ - { - id: 'security-solution', - name: 'indexpattern-datasource-current-indexpattern', - type: 'index-pattern', - }, { id: 'security-solution', name: 'indexpattern-datasource-layer-b1c3efc6-c886-4fba-978f-3b6bb5e7948a', type: 'index-pattern', }, - { id: 'security-solution', name: 'filter-index-pattern-0', type: 'index-pattern' }, ]); expect(mockNavigateToPrefilledEditor.mock.calls[0][1].openInNewTab).toEqual(true); }); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/actions.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/actions.tsx index 029f7545d3a42..0825aacfee410 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/actions.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/actions.tsx @@ -27,6 +27,7 @@ import { OPEN_IN_LENS, } from './translations'; import { VISUALIZATION_ACTIONS_BUTTON_CLASS } from './utils'; +import { SourcererScopeName } from '../../store/sourcerer/model'; const Wrapper = styled.div` &.viz-actions { @@ -41,8 +42,10 @@ const Wrapper = styled.div` `; const VisualizationActionsComponent: React.FC = ({ + applyGlobalQueriesAndFilters = true, className, extraActions, + extraOptions, getLensAttributes, inputId = InputsModelId.global, inspectIndex = 0, @@ -52,7 +55,8 @@ const VisualizationActionsComponent: React.FC = ({ onCloseInspect, queryId, timerange, - title, + title: inspectTitle, + scopeId = SourcererScopeName.default, stackByField, withDefaultActions = true, }) => { @@ -71,9 +75,13 @@ const VisualizationActionsComponent: React.FC = ({ }; const attributes = useLensAttributes({ - lensAttributes, + applyGlobalQueriesAndFilters, + extraOptions, getLensAttributes, + lensAttributes, + scopeId, stackByField, + title: '', }); const dataTestSubj = `stat-${queryId}`; @@ -250,7 +258,7 @@ const VisualizationActionsComponent: React.FC = ({ inputId={inputId} request={request} response={response} - title={title} + title={inspectTitle} /> )} diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/dns_top_domains.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/dns_top_domains.test.ts.snap index b25fced257c67..626e61fe0399a 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/dns_top_domains.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/dns_top_domains.test.ts.snap @@ -43,7 +43,7 @@ Object { "e8842815-2a45-4c74-86de-c19a391e2424": Object { "dataType": "string", "isBucketed": true, - "label": "Top values of dns.question.registered_domain", + "label": "Top values of event.dataset", "operationType": "terms", "params": Object { "accuracyMode": true, @@ -61,7 +61,7 @@ Object { "size": 10, }, "scale": "ordinal", - "sourceField": "dns.question.registered_domain", + "sourceField": "event.dataset", }, }, "incompleteColumns": Object {}, diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/dns_top_domains.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/dns_top_domains.ts index 87aae1a44dd3d..24f611eb4ebf6 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/dns_top_domains.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/dns_top_domains.ts @@ -9,7 +9,10 @@ import { TOP_VALUE, UNIQUE_COUNT } from '../../translations'; import type { LensAttributes, GetLensAttributes } from '../../types'; /* Exported from Kibana Saved Object */ -export const getDnsTopDomainsLensAttributes: GetLensAttributes = (stackByField, extraOptions) => +export const getDnsTopDomainsLensAttributes: GetLensAttributes = ( + stackByField = 'dns.question.registered_domain', + extraOptions +) => ({ title: 'Top domains by dns.question.registered_domain', visualizationType: 'lnsXY', @@ -98,11 +101,11 @@ export const getDnsTopDomainsLensAttributes: GetLensAttributes = (stackByField, 'b1c3efc6-c886-4fba-978f-3b6bb5e7948a': { columns: { 'e8842815-2a45-4c74-86de-c19a391e2424': { - label: TOP_VALUE('dns.question.registered_domain'), + label: TOP_VALUE(stackByField), dataType: 'string', operationType: 'terms', scale: 'ordinal', - sourceField: 'dns.question.registered_domain', + sourceField: stackByField, isBucketed: true, params: { size: 10, diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/types.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/types.ts index 732d23967e68c..732db46bf1cb2 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/types.ts @@ -30,8 +30,10 @@ export interface UseLensAttributesProps { } export interface VisualizationActionsProps { + applyGlobalQueriesAndFilters?: boolean; className?: string; extraActions?: Action[]; + extraOptions?: ExtraOptions; getLensAttributes?: GetLensAttributes; inputId?: InputsModelId.global | InputsModelId.timeline; inspectIndex?: number; @@ -40,6 +42,7 @@ export interface VisualizationActionsProps { lensAttributes?: LensAttributes | null; onCloseInspect?: () => void; queryId: string; + scopeId?: SourcererScopeName; stackByField?: string; timerange: { from: string; to: string }; title: React.ReactNode;