From c3822a7d4244a1de4d45549319d14ee4027f282c Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Tue, 28 Jun 2022 10:14:28 -0400 Subject: [PATCH 1/4] [Fleet[ Allow to specify a datastream id in the edit integration page --- .../common/types/models/package_policy.ts | 2 ++ .../components/datastream_hooks.tsx | 21 ++++++++------ .../components/datastream_mappings.tsx | 7 +++-- .../components/datastream_pipelines.test.tsx | 4 +-- .../components/datastream_pipelines.tsx | 7 +++-- .../components/steps/components/hooks.tsx | 18 ++++++++++++ .../components/package_policy_input_panel.tsx | 27 ++++++++++++------ .../package_policy_input_stream.tsx | 28 +++++++++++++++---- 8 files changed, 83 insertions(+), 31 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/hooks.tsx diff --git a/x-pack/plugins/fleet/common/types/models/package_policy.ts b/x-pack/plugins/fleet/common/types/models/package_policy.ts index 75932fd4a790a..c865d7ba8a9e7 100644 --- a/x-pack/plugins/fleet/common/types/models/package_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/package_policy.ts @@ -20,6 +20,7 @@ export interface PackagePolicyConfigRecordEntry { export type PackagePolicyConfigRecord = Record; export interface NewPackagePolicyInputStream { + id?: string; enabled: boolean; keep_enabled?: boolean; data_stream: { @@ -41,6 +42,7 @@ export interface PackagePolicyInputStream extends NewPackagePolicyInputStream { } export interface NewPackagePolicyInput { + id?: string; type: string; policy_template?: string; enabled: boolean; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_hooks.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_hooks.tsx index b8fd2fdcdc81d..d8e21fce7c306 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_hooks.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_hooks.tsx @@ -9,18 +9,21 @@ import { useRouteMatch } from 'react-router-dom'; import { useLink } from '../../../../hooks'; -export function usePackagePolicyEditorPageUrl() { +export function usePackagePolicyEditorPageUrl(dataStreamId?: string) { const { params: { packagePolicyId, policyId }, } = useRouteMatch<{ policyId: string; packagePolicyId: string }>(); const { getHref } = useLink(); - return packagePolicyId && policyId - ? getHref('edit_integration', { - policyId, - packagePolicyId, - }) - : getHref('integration_policy_edit', { - packagePolicyId, - }); + const baseUrl = + packagePolicyId && policyId + ? getHref('edit_integration', { + policyId, + packagePolicyId, + }) + : getHref('integration_policy_edit', { + packagePolicyId, + }); + + return `${baseUrl}${dataStreamId ? `?datastreamId=${encodeURIComponent(dataStreamId)}` : ''}`; } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_mappings.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_mappings.tsx index 8a2f056233041..af8703f43a525 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_mappings.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_mappings.tsx @@ -18,7 +18,7 @@ import { usePackagePolicyEditorPageUrl } from './datastream_hooks'; export interface PackagePolicyEditorDatastreamMappingsProps { packageInfo: PackageInfo; - dataStream: { dataset: string; type: string }; + packageInputStream: { id?: string; data_stream: { dataset: string; type: string } }; } function useComponentTemplates(dataStream: { dataset: string; type: string }) { @@ -35,8 +35,9 @@ function useComponentTemplates(dataStream: { dataset: string; type: string }) { export const PackagePolicyEditorDatastreamMappings: React.FunctionComponent< PackagePolicyEditorDatastreamMappingsProps -> = ({ dataStream, packageInfo }) => { - const pageUrl = usePackagePolicyEditorPageUrl(); +> = ({ packageInputStream, packageInfo }) => { + const dataStream = packageInputStream.data_stream; + const pageUrl = usePackagePolicyEditorPageUrl(packageInputStream.id); const { application, docLinks } = useStartServices(); const componentTemplateItems = useComponentTemplates(dataStream); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_pipelines.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_pipelines.test.tsx index a68facb62c3b4..9caea777a479d 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_pipelines.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_pipelines.test.tsx @@ -37,7 +37,7 @@ describe('DatastreamPipelines', () => { const result = renderer.render( { const result = renderer.render( = ({ dataStream, packageInfo }) => { +> = ({ packageInputStream, packageInfo }) => { + const dataStream = packageInputStream.data_stream; const { application, share, docLinks } = useStartServices(); const ingestPipelineLocator = share.url.locators.get('INGEST_PIPELINES_APP_LOCATOR'); - const pageUrl = usePackagePolicyEditorPageUrl(); + const pageUrl = usePackagePolicyEditorPageUrl(packageInputStream.id); const { pipelines, addPipelineUrl, hasCustom, isLoading } = useDatastreamIngestPipelines( packageInfo, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/hooks.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/hooks.tsx new file mode 100644 index 0000000000000..55b8ac90c749c --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/hooks.tsx @@ -0,0 +1,18 @@ +/* + * 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 { useMemo } from 'react'; +import { useHistory } from 'react-router-dom'; + +export function useDataStreamId() { + const history = useHistory(); + + return useMemo(() => { + const searchParams = new URLSearchParams(history.location.search); + return searchParams.get('datastreamId') ?? undefined; + }, [history.location.search]); +} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx index cc641812235ce..a45f076c0a25b 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx @@ -32,6 +32,7 @@ import { hasInvalidButRequiredVar, countValidationErrors } from '../../../servic import { PackagePolicyInputConfig } from './package_policy_input_config'; import { PackagePolicyInputStreamConfig } from './package_policy_input_stream'; +import { useDataStreamId } from './hooks'; const ShortenedHorizontalRule = styled(EuiHorizontalRule)` &&& { @@ -43,13 +44,13 @@ const ShortenedHorizontalRule = styled(EuiHorizontalRule)` const shouldShowStreamsByDefault = ( packageInput: RegistryInput, packageInputStreams: Array, - packagePolicyInput: NewPackagePolicyInput + packagePolicyInput: NewPackagePolicyInput, + defaultDataStreamId?: string ): boolean => { return ( - packagePolicyInput.enabled && - (hasInvalidButRequiredVar(packageInput.vars, packagePolicyInput.vars) || - Boolean( - packageInputStreams.find( + (packagePolicyInput.enabled && + (hasInvalidButRequiredVar(packageInput.vars, packagePolicyInput.vars) || + packageInputStreams.some( (stream) => stream.enabled && hasInvalidButRequiredVar( @@ -58,8 +59,10 @@ const shouldShowStreamsByDefault = ( (pkgStream) => stream.data_stream.dataset === pkgStream.data_stream.dataset )?.vars ) - ) - )) + ))) || + packagePolicyInput.streams.some((stream) => { + return defaultDataStreamId && stream.id && stream.id === defaultDataStreamId; + }) ); }; @@ -83,9 +86,15 @@ export const PackagePolicyInputPanel: React.FunctionComponent<{ inputValidationResults, forceShowErrors, }) => { + const defaultDataStreamId = useDataStreamId(); // Showing streams toggle state - const [isShowingStreams, setIsShowingStreams] = useState( - shouldShowStreamsByDefault(packageInput, packageInputStreams, packagePolicyInput) + const [isShowingStreams, setIsShowingStreams] = useState(() => + shouldShowStreamsByDefault( + packageInput, + packageInputStreams, + packagePolicyInput, + defaultDataStreamId + ) ); // Errors state diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx index 54b3c1ac70cbe..7886e294a2961 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState, Fragment, memo, useMemo } from 'react'; +import React, { useState, Fragment, memo, useMemo, useEffect, useRef } from 'react'; import ReactMarkdown from 'react-markdown'; import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -33,11 +33,16 @@ import { PackagePolicyEditorDatastreamPipelines } from '../../datastream_pipelin import { PackagePolicyEditorDatastreamMappings } from '../../datastream_mappings'; import { PackagePolicyInputVarField } from './package_policy_input_var_field'; +import { useDataStreamId } from './hooks'; const FlexItemWithMaxWidth = styled(EuiFlexItem)` max-width: calc(50% - ${(props) => props.theme.eui.euiSizeL}); `; +const ScrollAnchor = styled.div` + scroll-margin-top: ${(props) => parseFloat(props.theme.eui.euiHeaderHeightCompensation) * 2}px; +`; + export const PackagePolicyInputStreamConfig: React.FunctionComponent<{ packagePolicy: NewPackagePolicy; packageInputStream: RegistryStream & { data_stream: { dataset: string; type: string } }; @@ -59,11 +64,23 @@ export const PackagePolicyInputStreamConfig: React.FunctionComponent<{ const { params: { packagePolicyId }, } = useRouteMatch<{ packagePolicyId?: string }>(); + const defaultDataStreamId = useDataStreamId(); + const containerRef = useRef(null); + const isDefaultDatstream = + !!defaultDataStreamId && + !!packagePolicyInputStream.id && + packagePolicyInputStream.id === defaultDataStreamId; const isPackagePolicyEdit = !!packagePolicyId; + useEffect(() => { + if (isDefaultDatstream && containerRef.current) { + containerRef.current.scrollIntoView(); + } + }, [isDefaultDatstream, containerRef]); + // Showing advanced options toggle state - const [isShowingAdvanced, setIsShowingAdvanced] = useState(); + const [isShowingAdvanced, setIsShowingAdvanced] = useState(isDefaultDatstream); // Errors state const hasErrors = forceShowErrors && validationHasErrors(inputStreamValidationResults); @@ -94,7 +111,8 @@ export const PackagePolicyInputStreamConfig: React.FunctionComponent<{ ); return ( - + + @@ -218,13 +236,13 @@ export const PackagePolicyInputStreamConfig: React.FunctionComponent<{ <> From 94946f71aaadef86b60f599e36dde43691f69d80 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Tue, 28 Jun 2022 14:18:31 -0400 Subject: [PATCH 2/4] fix history --- .../single_page_layout/index.test.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx index ab096022823b3..427db14295a36 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx @@ -84,6 +84,9 @@ jest.mock('react-router-dom', () => ({ useLocation: jest.fn().mockReturnValue({ search: '' }), useHistory: jest.fn().mockReturnValue({ push: jest.fn(), + location: { + search: '', + }, }), })); From dcff25a993638f4f195a058513f7236ac5f25827 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Tue, 28 Jun 2022 14:39:29 -0400 Subject: [PATCH 3/4] add step on edit mapping --- .../components/datastream_mappings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_mappings.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_mappings.tsx index af8703f43a525..33ae9c7b44c32 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_mappings.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_mappings.tsx @@ -100,7 +100,7 @@ export const PackagePolicyEditorDatastreamMappings: React.FunctionComponent< path: `/data/index_management/edit_component_template/${el.templateName}`, }); - application.navigateToUrl(`${url}?redirect_path=${pageUrl}`); + application.navigateToUrl(`${url}?step=mappings&redirect_path=${pageUrl}`); }, available: ({ canEdit }) => !!canEdit, }, From 52624a77f3a384d8644dc945aa8bea68f5ba6f5b Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Thu, 30 Jun 2022 08:26:43 -0400 Subject: [PATCH 4/4] Add unit tests --- .../components/datastream_hooks.test.tsx | 54 ++++++++++++++++ .../package_policy_input_panel.test.tsx | 61 +++++++++++++++++++ .../components/package_policy_input_panel.tsx | 29 +++++---- 3 files changed, 131 insertions(+), 13 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_hooks.test.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.test.tsx diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_hooks.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_hooks.test.tsx new file mode 100644 index 0000000000000..d903125386e0f --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/datastream_hooks.test.tsx @@ -0,0 +1,54 @@ +/* + * 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 { useRouteMatch } from 'react-router-dom'; + +import { createFleetTestRendererMock } from '../../../../../../mock'; + +import { usePackagePolicyEditorPageUrl } from './datastream_hooks'; + +const mockedUseRouteMatch = useRouteMatch as jest.MockedFunction; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useRouteMatch: jest.fn(), +})); + +describe('usePackagePolicyEditorPageUrl', () => { + it('should render an integration url if no policy id is provided', () => { + const renderer = createFleetTestRendererMock(); + mockedUseRouteMatch.mockReturnValue({ + params: { packagePolicyId: 'test-package-policy-id' }, + } as any); + const { result } = renderer.renderHook(() => usePackagePolicyEditorPageUrl()); + expect(result.current).toBe('/mock/app/integrations/edit-integration/test-package-policy-id'); + }); + + it('should render a fleet url if a policy id is provided', () => { + const renderer = createFleetTestRendererMock(); + mockedUseRouteMatch.mockReturnValue({ + params: { policyId: 'policy1', packagePolicyId: 'test-package-policy-id' }, + } as any); + const { result } = renderer.renderHook(() => usePackagePolicyEditorPageUrl()); + expect(result.current).toBe( + '/mock/app/fleet/policies/policy1/edit-integration/test-package-policy-id' + ); + }); + + it('should add datastream Id if provided', () => { + const renderer = createFleetTestRendererMock(); + mockedUseRouteMatch.mockReturnValue({ + params: { policyId: 'policy1', packagePolicyId: 'test-package-policy-id' }, + } as any); + const { result } = renderer.renderHook(() => + usePackagePolicyEditorPageUrl('test-datastream-id') + ); + expect(result.current).toBe( + '/mock/app/fleet/policies/policy1/edit-integration/test-package-policy-id?datastreamId=test-datastream-id' + ); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.test.tsx new file mode 100644 index 0000000000000..9988ac1f7c22c --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.test.tsx @@ -0,0 +1,61 @@ +/* + * 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 { shouldShowStreamsByDefault } from './package_policy_input_panel'; + +describe('shouldShowStreamsByDefault', () => { + it('should return true if a datastreamId is provided and contained in the input', () => { + const res = shouldShowStreamsByDefault( + {} as any, + [], + { + enabled: true, + streams: [ + { + id: 'datastream-id', + }, + ], + } as any, + 'datastream-id' + ); + expect(res).toBeTruthy(); + }); + + it('should return false if a datastreamId is provided but not contained in the input', () => { + const res = shouldShowStreamsByDefault( + {} as any, + [], + { + enabled: true, + streams: [ + { + id: 'datastream-1', + }, + ], + } as any, + 'datastream-id' + ); + expect(res).toBeFalsy(); + }); + + it('should return false if a datastreamId is provided but the input is disabled', () => { + const res = shouldShowStreamsByDefault( + {} as any, + [], + { + enabled: false, + streams: [ + { + id: 'datastream-id', + }, + ], + } as any, + 'datastream-id' + ); + expect(res).toBeFalsy(); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx index a45f076c0a25b..b34ce95297761 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx @@ -41,25 +41,28 @@ const ShortenedHorizontalRule = styled(EuiHorizontalRule)` } `; -const shouldShowStreamsByDefault = ( +export const shouldShowStreamsByDefault = ( packageInput: RegistryInput, packageInputStreams: Array, packagePolicyInput: NewPackagePolicyInput, defaultDataStreamId?: string ): boolean => { + if (!packagePolicyInput.enabled) { + return false; + } + return ( - (packagePolicyInput.enabled && - (hasInvalidButRequiredVar(packageInput.vars, packagePolicyInput.vars) || - packageInputStreams.some( - (stream) => - stream.enabled && - hasInvalidButRequiredVar( - stream.vars, - packagePolicyInput.streams.find( - (pkgStream) => stream.data_stream.dataset === pkgStream.data_stream.dataset - )?.vars - ) - ))) || + hasInvalidButRequiredVar(packageInput.vars, packagePolicyInput.vars) || + packageInputStreams.some( + (stream) => + stream.enabled && + hasInvalidButRequiredVar( + stream.vars, + packagePolicyInput.streams.find( + (pkgStream) => stream.data_stream.dataset === pkgStream.data_stream.dataset + )?.vars + ) + ) || packagePolicyInput.streams.some((stream) => { return defaultDataStreamId && stream.id && stream.id === defaultDataStreamId; })