From 401ad5273519f3253c513e2585774d2e15e5ddbd Mon Sep 17 00:00:00 2001 From: Tre Date: Thu, 7 Apr 2022 17:03:37 +0100 Subject: [PATCH 01/42] [QA][Code Coverage] Drop dead shell scripts, update docs (#129565) While searching the ingestion system for any [upcoming deprecations](https://github.com/elastic/kibana/pull/129087), I noticed these scripts are no longer used. --- .../docs/team_assignment/README.md | 8 ++-- .../shell_scripts/copy_jest_report.sh | 10 ---- .../shell_scripts/extract_archives.sh | 12 ----- .../shell_scripts/extract_xpack_tests.sh | 7 --- ...te_team_assignments_and_ingest_coverage.sh | 47 ------------------- .../shell_scripts/merge_functional.sh | 24 ---------- 6 files changed, 5 insertions(+), 103 deletions(-) delete mode 100755 src/dev/code_coverage/shell_scripts/copy_jest_report.sh delete mode 100644 src/dev/code_coverage/shell_scripts/extract_archives.sh delete mode 100644 src/dev/code_coverage/shell_scripts/extract_xpack_tests.sh delete mode 100644 src/dev/code_coverage/shell_scripts/generate_team_assignments_and_ingest_coverage.sh delete mode 100755 src/dev/code_coverage/shell_scripts/merge_functional.sh diff --git a/src/dev/code_coverage/docs/team_assignment/README.md b/src/dev/code_coverage/docs/team_assignment/README.md index bef4e74415b5a..c5b0db830ec4b 100644 --- a/src/dev/code_coverage/docs/team_assignment/README.md +++ b/src/dev/code_coverage/docs/team_assignment/README.md @@ -2,9 +2,11 @@ Team assignment occurs once per ci run. -The "orchestration" entry point is a [Jenkinsfile Scripted Pipeline](https://github.com/elastic/kibana/blob/f73bc48b3bbbb5ad2042c1aa267aea2150b7b742/.ci/Jenkinsfile_coverage#L21) -This Jenkinsfile runs a [shell script](https://github.com/elastic/kibana/blob/main/src/dev/code_coverage/shell_scripts/generate_team_assignments_and_ingest_coverage.sh#L33) that kicks everything off. -The end result is the data is ingested to our [Kibana Stats Cluster](https://kibana-stats.elastic.dev/app/dashboards#/view/58b8db70-62f9-11ea-8312-7f2d69b79843?_g=(filters%3A!()%2CrefreshInterval%3A(pause%3A!t%2Cvalue%3A0)%2Ctime%3A(from%3Anow-7d%2Cto%3Anow))) +The entry point is a [yaml config](https://buildkite.com/elastic/kibana-code-coverage-main/steps) +Within the yaml config, [this shell script](https://github.com/elastic/kibana/blob/main/.buildkite/scripts/steps/code_coverage/reporting/ingestData.sh) is used. +Search for `node scripts/generate_team_assignments.js` and you'll see how it is generated before ingestion begins. +The generated file's path is passed to the ingestion system via an environment variable: `--teamAssignmentsPath $TEAM_ASSIGN_PATH` +The ingestion system uses it [here](https://github.com/elastic/kibana/blob/main/src/dev/code_coverage/ingest_coverage/index.js#L33). ## Team Assignment Parsing (from .github/CODEOWNERS) We add additional metadata to the CODEOWNERS file. diff --git a/src/dev/code_coverage/shell_scripts/copy_jest_report.sh b/src/dev/code_coverage/shell_scripts/copy_jest_report.sh deleted file mode 100755 index 8369d5b467c02..0000000000000 --- a/src/dev/code_coverage/shell_scripts/copy_jest_report.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -EXTRACT_START_DIR=tmp/extracted_coverage -EXTRACT_END_DIR=target/kibana-coverage -COMBINED_EXRACT_DIR=/${EXTRACT_START_DIR}/${EXTRACT_END_DIR} - - -echo "### Copy combined jest report" -mkdir -p $EXTRACT_END_DIR/jest-combined -cp -r $COMBINED_EXRACT_DIR/jest-combined/. $EXTRACT_END_DIR/jest-combined/ diff --git a/src/dev/code_coverage/shell_scripts/extract_archives.sh b/src/dev/code_coverage/shell_scripts/extract_archives.sh deleted file mode 100644 index 14b35f8786d02..0000000000000 --- a/src/dev/code_coverage/shell_scripts/extract_archives.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -DOWNLOAD_DIR=/tmp/downloaded_coverage -EXTRACT_DIR=/tmp/extracted_coverage - -mkdir -p $EXTRACT_DIR - -echo "### Extracting downloaded artifacts" -for x in kibana-intake kibana-oss-tests kibana-xpack-tests; do - tar -xzf $DOWNLOAD_DIR/coverage/${x}/kibana-coverage.tar.gz -C $EXTRACT_DIR || echo "### Error 'tarring': ${x}" -done - diff --git a/src/dev/code_coverage/shell_scripts/extract_xpack_tests.sh b/src/dev/code_coverage/shell_scripts/extract_xpack_tests.sh deleted file mode 100644 index 29184584b726f..0000000000000 --- a/src/dev/code_coverage/shell_scripts/extract_xpack_tests.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -DOWNLOAD_DIR=/tmp/downloaded_coverage -EXTRACT_DIR=/tmp/extracted_coverage - -echo "### Extracting kibana-xpack-tests" -tar -xzf $DOWNLOAD_DIR/coverage/kibana-xpack-tests/kibana-coverage.tar.gz -C $EXTRACT_DIR diff --git a/src/dev/code_coverage/shell_scripts/generate_team_assignments_and_ingest_coverage.sh b/src/dev/code_coverage/shell_scripts/generate_team_assignments_and_ingest_coverage.sh deleted file mode 100644 index b7d6a4abce344..0000000000000 --- a/src/dev/code_coverage/shell_scripts/generate_team_assignments_and_ingest_coverage.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash - -echo "### Ingesting Code Coverage" -echo "" - -COVERAGE_JOB_NAME=$1 -export COVERAGE_JOB_NAME -echo "### debug COVERAGE_JOB_NAME: ${COVERAGE_JOB_NAME}" - -BUILD_ID=$2 -export BUILD_ID - -CI_RUN_URL=$3 -export CI_RUN_URL -echo "### debug CI_RUN_URL: ${CI_RUN_URL}" - -FETCHED_PREVIOUS=$4 -export FETCHED_PREVIOUS -echo "### debug FETCHED_PREVIOUS: ${FETCHED_PREVIOUS}" - -ES_HOST="https://${USER_FROM_VAULT}:${PASS_FROM_VAULT}@${HOST_FROM_VAULT}" -export ES_HOST - -STATIC_SITE_URL_BASE='https://kibana-coverage.elastic.dev' -export STATIC_SITE_URL_BASE - -TEAM_ASSIGN_PATH=$5 - -BUFFER_SIZE=500 -export BUFFER_SIZE - -# Build team assignments dat file -node scripts/generate_team_assignments.js --verbose --src .github/CODEOWNERS --dest $TEAM_ASSIGN_PATH - -# Need to override COVERAGE_INGESTION_KIBANA_ROOT since json file has original intake worker path -export COVERAGE_INGESTION_KIBANA_ROOT=/dev/shm/workspace/kibana - -for x in functional jest; do - echo "### Ingesting coverage for ${x}" - COVERAGE_SUMMARY_FILE=target/kibana-coverage/${x}-combined/coverage-summary.json - # running in background to speed up ingestion - node scripts/ingest_coverage.js --path ${COVERAGE_SUMMARY_FILE} --vcsInfoPath ./VCS_INFO.txt --teamAssignmentsPath $TEAM_ASSIGN_PATH & -done -wait - -echo "### Ingesting Code Coverage - Complete" -echo "" diff --git a/src/dev/code_coverage/shell_scripts/merge_functional.sh b/src/dev/code_coverage/shell_scripts/merge_functional.sh deleted file mode 100755 index 68370910e6ad7..0000000000000 --- a/src/dev/code_coverage/shell_scripts/merge_functional.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -COVERAGE_TEMP_DIR=/tmp/extracted_coverage/target/kibana-coverage/ -export COVERAGE_TEMP_DIR - -checkoutDir="$(pwd)" -echo "### checkoutDir=${checkoutDir}" - -coverageBasePath="/dev/shm/workspace" -echo "### Clone kibana to ${coverageBasePath}" -mkdir -p "$coverageBasePath/kibana" -rsync -ahSD --ignore-errors --force --delete --stats ./ "$coverageBasePath/kibana/" -cd "$coverageBasePath/kibana" - -echo "### bootstrap" -yarn kbn bootstrap -echo "### Merge coverage reports" -yarn nyc report --nycrc-path src/dev/code_coverage/nyc_config/nyc.functional.config.js - -echo "### Copy 'target' to ${checkoutDir}" -rsync -ahSD --ignore-errors --force --delete --stats target "$checkoutDir/" - -echo "### Back to $checkoutDir" -cd "$checkoutDir" From a27ae079e11b1d0719e7cb5bad1d9b8934c82579 Mon Sep 17 00:00:00 2001 From: Garrett Spong Date: Thu, 7 Apr 2022 10:16:08 -0600 Subject: [PATCH 02/42] [Security Solution][Detections] Fixes utility bar text overflow issue on Rule Execution Log (#129752) ## Summary Resolves https://github.com/elastic/kibana/issues/129321#issuecomment-1091115109 #### Before:

#### After:

### Checklist Delete any items that are not applicable to this PR. - [X] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) --- .../execution_log_table.test.tsx.snap | 20 +++++++------- .../execution_log_table.tsx | 26 ++++++++++--------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/__snapshots__/execution_log_table.test.tsx.snap b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/__snapshots__/execution_log_table.test.tsx.snap index 62621e2a2e0dc..f93cce70172dc 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/__snapshots__/execution_log_table.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/__snapshots__/execution_log_table.test.tsx.snap @@ -42,15 +42,17 @@ exports[`ExecutionLogTable snapshots renders correctly against snapshot 1`] = ` - - + + + + = ({ )} - - {timelines.getLastUpdated({ - showUpdating: isLoading || isFetching, - updatedAt: dataUpdatedAt, - })} - - onShowMetricColumnsCallback(e.target.checked)} - /> + + + {timelines.getLastUpdated({ + showUpdating: isLoading || isFetching, + updatedAt: dataUpdatedAt, + })} + + onShowMetricColumnsCallback(e.target.checked)} + /> + Date: Thu, 7 Apr 2022 12:04:01 -0500 Subject: [PATCH 03/42] [build] Only use buildx if cross compiling (#128560) * [build] Only use buildx if cross compiling * reuse variable * fix conditional --- src/dev/build/tasks/os_packages/docker_generator/run.ts | 1 + .../tasks/os_packages/docker_generator/template_context.ts | 1 + .../docker_generator/templates/build_docker_sh.template.ts | 6 +++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/dev/build/tasks/os_packages/docker_generator/run.ts b/src/dev/build/tasks/os_packages/docker_generator/run.ts index 3152f07628fc9..c406fe47c1542 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/run.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/run.ts @@ -92,6 +92,7 @@ export async function runDockerGenerator( dockerTargetFilename, dockerPush, dockerTagQualifier, + dockerCrossCompile, baseOSImage, dockerBuildDate, ubi: flags.ubi, diff --git a/src/dev/build/tasks/os_packages/docker_generator/template_context.ts b/src/dev/build/tasks/os_packages/docker_generator/template_context.ts index 524cfcef18284..35977d47aaaa7 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/template_context.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/template_context.ts @@ -16,6 +16,7 @@ export interface TemplateContext { artifactsDir: string; dockerPush: boolean; dockerTagQualifier: string | null; + dockerCrossCompile: boolean; imageTag: string; dockerBuildDir: string; dockerTargetFilename: string; diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.ts b/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.ts index a14de2a0581ff..316428d46a957 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.ts @@ -15,6 +15,7 @@ function generator({ imageFlavor, dockerPush, dockerTagQualifier, + dockerCrossCompile, version, dockerTargetFilename, baseOSImage, @@ -24,6 +25,9 @@ function generator({ dockerTagQualifier ? '-' + dockerTagQualifier : '' }`; const dockerArchitecture = architecture === 'aarch64' ? 'linux/arm64' : 'linux/amd64'; + const dockerBuild = dockerCrossCompile + ? `docker buildx build --platform ${dockerArchitecture} -t ${dockerTargetName} -f Dockerfile . || exit 1;` + : `docker build -t ${dockerTargetName} -f Dockerfile . || exit 1;`; return dedent(` #!/usr/bin/env bash # @@ -60,7 +64,7 @@ function generator({ retry_docker_pull ${baseOSImage} echo "Building: kibana${imageFlavor}-docker"; \\ - docker buildx build --platform ${dockerArchitecture} -t ${dockerTargetName} -f Dockerfile . || exit 1; + ${dockerBuild} docker save ${dockerTargetName} | gzip -c > ${dockerTargetFilename} From 9f305dafd89bfd69f8a20f5d24456dea9b9c593c Mon Sep 17 00:00:00 2001 From: Kevin Qualters <56408403+kqualters-elastic@users.noreply.github.com> Date: Thu, 7 Apr 2022 14:10:47 -0400 Subject: [PATCH 04/42] [Security Solution] [Session View] Session view detail panel and timeline side panel css cleanup (#129690) * Make session detail content scroll if needed * Remove unneeded importants and hard coded light colors, make theme aware * Use full screen styles, make the dualing flyouts work together * Remove unused import --- .../timeline/session_tab_content/index.tsx | 47 ++++++++++++------- .../use_session_view.test.tsx | 22 ++++++++- .../session_tab_content/use_session_view.tsx | 9 +++- .../timeline/tabs_content/index.tsx | 15 +++--- .../public/components/session_view/styles.ts | 15 +++--- x-pack/plugins/session_view/public/types.ts | 2 +- 6 files changed, 76 insertions(+), 34 deletions(-) diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/index.tsx index a12b377fd3a22..8035dda95081f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/index.tsx @@ -5,22 +5,19 @@ * 2.0. */ -import React from 'react'; +import React, { useState, useCallback } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import styled from 'styled-components'; import { TimelineId } from '../../../../../common/types/timeline'; import { useSessionView } from './use_session_view'; -const FullWidthFlexGroup = styled(EuiFlexGroup)` - margin: 0; - width: 100%; - overflow: hidden; +const FlexItemWithMargin = styled(EuiFlexItem)` + ${({ theme }) => `margin: 0 ${theme.eui.euiSizeM};`} `; -const ScrollableFlexItem = styled(EuiFlexItem)` - ${({ theme }) => `margin: 0 ${theme.eui.euiSizeM};`} - overflow: hidden; - width: 100%; +const MaxWidthPageFlexGroup = styled(EuiFlexGroup)` + max-width: 100%; + box-sizing: border-box; `; const VerticalRule = styled.div` @@ -29,28 +26,46 @@ const VerticalRule = styled.div` background: ${({ theme }) => theme.eui.euiColorLightShade}; `; +const StyledFlexItem = styled(EuiFlexItem)` + ${({ theme }) => `margin: 0 ${theme.eui.euiSizeM};`} + height: 100%; + min-width: 320px; +`; + interface Props { timelineId: TimelineId; } const SessionTabContent: React.FC = ({ timelineId }) => { + const [height, setHeight] = useState(0); + const measuredRef = useCallback((node) => { + if (node !== null) { + setHeight(node.getBoundingClientRect().height); + } + }, []); const { SessionView, shouldShowDetailsPanel, DetailsPanel, Navigation } = useSessionView({ timelineId, + height, }); return ( - - - {Navigation} - {SessionView} - + + + {Navigation} + {SessionView} + {shouldShowDetailsPanel && ( <> - {DetailsPanel} + {DetailsPanel} )} - + ); }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/use_session_view.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/use_session_view.test.tsx index 110e581a4f6a5..3e1f087a0d726 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/use_session_view.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/use_session_view.test.tsx @@ -72,10 +72,12 @@ jest.mock('../../../../common/lib/kibana', () => { }), }; }); +const mockDetails = () => {}; + jest.mock('../../side_panel/hooks/use_detail_panel', () => { return { useDetailPanel: () => ({ - openDetailsPanel: () => {}, + openDetailsPanel: mockDetails, handleOnDetailsPanelClosed: () => {}, DetailsPanel: () =>
, shouldShowDetailsPanel: false, @@ -146,6 +148,24 @@ describe('useSessionView with active timeline and a session id and graph event i expect(renderResult.getByText('Close session')).toBeTruthy(); }); + it('uses an optional height when passed', () => { + renderHook( + () => { + const testProps = { + timelineId: TimelineId.test, + height: 1118, + }; + return useSessionView(testProps); + }, + { wrapper: Wrapper } + ); + expect(kibana.services.sessionView.getSessionView).toHaveBeenCalledWith({ + height: 1000, + sessionEntityId: 'test', + loadAlertDetails: mockDetails, + }); + }); + describe('useSessionView with non active timeline and graph event id set', () => { beforeEach(() => { setTimelineFullScreen = jest.fn(); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/use_session_view.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/use_session_view.tsx index 9c4261a5317ed..370509e4c88dc 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/use_session_view.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/use_session_view.tsx @@ -109,9 +109,11 @@ const Navigation = React.memo(NavigationComponent); export const useSessionView = ({ timelineId, entityType, + height, }: { timelineId: TimelineId; entityType?: EntityType; + height?: number; }) => { const { sessionView } = useKibana().services; const dispatch = useDispatch(); @@ -199,18 +201,21 @@ export const useSessionView = ({ entityType, sourcererScope, timelineId, - tabType: timelineId === TimelineId.active ? TimelineTabs.session : TimelineTabs.query, + tabType: timelineId === TimelineId.active ? activeTab : TimelineTabs.query, }); const sessionViewComponent = useMemo(() => { + const sessionViewSearchBarHeight = 118; + const heightMinusSearchBar = height ? height - sessionViewSearchBarHeight : undefined; return sessionViewConfig !== null ? sessionView.getSessionView({ ...sessionViewConfig, loadAlertDetails: openDetailsPanel, isFullScreen: fullScreen, + height: heightMinusSearchBar, }) : null; - }, [fullScreen, openDetailsPanel, sessionView, sessionViewConfig]); + }, [fullScreen, openDetailsPanel, sessionView, sessionViewConfig, height]); const navigation = useMemo(() => { return ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx index f25e86d4eebf6..2f5262c7c598f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx @@ -38,13 +38,15 @@ import { } from './selectors'; import * as i18n from './translations'; -const HideShowContainer = styled.div.attrs<{ $isVisible: boolean }>(({ $isVisible = false }) => ({ - style: { - display: $isVisible ? 'flex' : 'none', - }, -}))<{ $isVisible: boolean }>` +const HideShowContainer = styled.div.attrs<{ $isVisible: boolean; isOverflowYScroll: boolean }>( + ({ $isVisible = false, isOverflowYScroll = false }) => ({ + style: { + display: $isVisible ? 'flex' : 'none', + overflow: isOverflowYScroll ? 'hidden scroll' : 'hidden', + }, + }) +)<{ $isVisible: boolean; isOverflowYScroll?: boolean }>` flex: 1; - overflow: hidden; `; const QueryTabContent = lazy(() => import('../query_tab_content')); @@ -198,6 +200,7 @@ const ActiveTimelineTab = memo( )} {isGraphOrNotesTabs && getTab(activeTimelineTab)} diff --git a/x-pack/plugins/session_view/public/components/session_view/styles.ts b/x-pack/plugins/session_view/public/components/session_view/styles.ts index 0812796632936..b78ed74102785 100644 --- a/x-pack/plugins/session_view/public/components/session_view/styles.ts +++ b/x-pack/plugins/session_view/public/components/session_view/styles.ts @@ -8,22 +8,21 @@ import { useMemo } from 'react'; import { useEuiTheme } from '@elastic/eui'; import { CSSObject } from '@emotion/react'; -import { euiLightVars as theme } from '@kbn/ui-theme'; interface StylesDeps { - height?: string; + height?: number; isFullScreen?: boolean; } -export const useStyles = ({ height = '500px', isFullScreen }: StylesDeps) => { +export const useStyles = ({ height = 500, isFullScreen }: StylesDeps) => { const { euiTheme } = useEuiTheme(); const cached = useMemo(() => { - const { border } = euiTheme; + const { border, colors } = euiTheme; // 118px = Session View Toolbar height + Close Session button height + spacing margin at the bottom const sessionView: CSSObject = { - height: `${isFullScreen ? 'calc(100vh - 118px)' : height}`, + height: `${isFullScreen ? 'calc(100vh - 118px)' : height + 'px'}`, }; const processTree: CSSObject = { @@ -47,11 +46,11 @@ export const useStyles = ({ height = '500px', isFullScreen }: StylesDeps) => { }; const searchBar: CSSObject = { position: 'relative', - margin: `${euiTheme.size.m} ${euiTheme.size.xs} !important`, + margin: `${euiTheme.size.m} ${euiTheme.size.xs}`, }; const buttonsEyeDetail: CSSObject = { - margin: `${euiTheme.size.m} ${euiTheme.size.xs} !important`, + margin: `${euiTheme.size.m} ${euiTheme.size.xs}`, }; const sessionViewerComponent: CSSObject = { @@ -60,7 +59,7 @@ export const useStyles = ({ height = '500px', isFullScreen }: StylesDeps) => { }; const toolBar: CSSObject = { - backgroundColor: `${theme.euiFormBackgroundDisabledColor} !important`, + backgroundColor: `${colors.emptyShade}`, }; return { diff --git a/x-pack/plugins/session_view/public/types.ts b/x-pack/plugins/session_view/public/types.ts index 4bb8f15160778..1dcfe4f47c069 100644 --- a/x-pack/plugins/session_view/public/types.ts +++ b/x-pack/plugins/session_view/public/types.ts @@ -13,7 +13,7 @@ export type SessionViewServices = CoreStart; export interface SessionViewDeps { // the root node of the process tree to render. e.g process.entry.entity_id or process.session_leader.entity_id sessionEntityId: string; - height?: string; + height?: number; isFullScreen?: boolean; // if provided, the session view will jump to and select the provided event if it belongs to the session leader // session view will fetch a page worth of events starting from jumpToEvent as well as a page backwards. From ae68c1c3d39a2c979f75b59b2262320b23e17a06 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 7 Apr 2022 13:47:46 -0500 Subject: [PATCH 05/42] [Discover] account for hidden time column in default sort (#129659) --- .../main/utils/get_state_defaults.ts | 7 ++++++- .../components/table_header/table_header.tsx | 4 +++- .../doc_table/lib/get_default_sort.test.ts | 13 ++++++++---- .../doc_table/lib/get_default_sort.ts | 9 ++++++-- .../embeddable/saved_search_embeddable.tsx | 21 ++++++++----------- 5 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/plugins/discover/public/application/main/utils/get_state_defaults.ts b/src/plugins/discover/public/application/main/utils/get_state_defaults.ts index 807988ddf36d2..56197218355cd 100644 --- a/src/plugins/discover/public/application/main/utils/get_state_defaults.ts +++ b/src/plugins/discover/public/application/main/utils/get_state_defaults.ts @@ -10,6 +10,7 @@ import { cloneDeep, isEqual } from 'lodash'; import { IUiSettingsClient } from 'kibana/public'; import { DEFAULT_COLUMNS_SETTING, + DOC_HIDE_TIME_COLUMN_SETTING, SEARCH_FIELDS_FROM_SOURCE, SORT_DEFAULT_ORDER_SETTING, } from '../../../../common'; @@ -53,7 +54,11 @@ export function getStateDefaults({ const defaultState = { query, sort: !sort.length - ? getDefaultSort(indexPattern, config.get(SORT_DEFAULT_ORDER_SETTING, 'desc')) + ? getDefaultSort( + indexPattern, + config.get(SORT_DEFAULT_ORDER_SETTING, 'desc'), + config.get(DOC_HIDE_TIME_COLUMN_SETTING, false) + ) : sort, columns, index: indexPattern?.id, diff --git a/src/plugins/discover/public/components/doc_table/components/table_header/table_header.tsx b/src/plugins/discover/public/components/doc_table/components/table_header/table_header.tsx index 9dd044ed1d2ae..f1e594cb8b9c8 100644 --- a/src/plugins/discover/public/components/doc_table/components/table_header/table_header.tsx +++ b/src/plugins/discover/public/components/doc_table/components/table_header/table_header.tsx @@ -54,7 +54,9 @@ export function TableHeader({ customLabel={indexPattern.getFieldByName(col.name)?.customLabel} isTimeColumn={indexPattern.timeFieldName === col.name} sortOrder={ - sortOrder.length ? sortOrder : getDefaultSort(indexPattern, defaultSortOrder) + sortOrder.length + ? sortOrder + : getDefaultSort(indexPattern, defaultSortOrder, hideTimeColumn) } onMoveColumn={onMoveColumn} onRemoveColumn={onRemoveColumn} diff --git a/src/plugins/discover/public/components/doc_table/lib/get_default_sort.test.ts b/src/plugins/discover/public/components/doc_table/lib/get_default_sort.test.ts index ae45607a91b50..c468301060400 100644 --- a/src/plugins/discover/public/components/doc_table/lib/get_default_sort.test.ts +++ b/src/plugins/discover/public/components/doc_table/lib/get_default_sort.test.ts @@ -18,12 +18,17 @@ describe('getDefaultSort function', function () { }); test('should return default sort for an index pattern with timeFieldName', function () { - expect(getDefaultSort(stubDataView, 'desc')).toEqual([['@timestamp', 'desc']]); - expect(getDefaultSort(stubDataView, 'asc')).toEqual([['@timestamp', 'asc']]); + expect(getDefaultSort(stubDataView, 'desc', false)).toEqual([['@timestamp', 'desc']]); + expect(getDefaultSort(stubDataView, 'asc', false)).toEqual([['@timestamp', 'asc']]); }); test('should return default sort for an index pattern without timeFieldName', function () { - expect(getDefaultSort(stubDataViewWithoutTimeField, 'desc')).toEqual([]); - expect(getDefaultSort(stubDataViewWithoutTimeField, 'asc')).toEqual([]); + expect(getDefaultSort(stubDataViewWithoutTimeField, 'desc', false)).toEqual([]); + expect(getDefaultSort(stubDataViewWithoutTimeField, 'asc', false)).toEqual([]); + }); + + test('should return empty sort for data view when time column is hidden', function () { + expect(getDefaultSort(stubDataView, 'desc', true)).toEqual([]); + expect(getDefaultSort(stubDataView, 'asc', true)).toEqual([]); }); }); diff --git a/src/plugins/discover/public/components/doc_table/lib/get_default_sort.ts b/src/plugins/discover/public/components/doc_table/lib/get_default_sort.ts index a100aeeff3493..341d028206747 100644 --- a/src/plugins/discover/public/components/doc_table/lib/get_default_sort.ts +++ b/src/plugins/discover/public/components/doc_table/lib/get_default_sort.ts @@ -16,9 +16,14 @@ import { SortOrder } from '../components/table_header/helpers'; */ export function getDefaultSort( indexPattern: DataView | undefined, - defaultSortOrder: string = 'desc' + defaultSortOrder: string = 'desc', + hidingTimeColumn: boolean = false ): SortOrder[] { - if (indexPattern?.timeFieldName && isSortable(indexPattern.timeFieldName, indexPattern)) { + if ( + indexPattern?.timeFieldName && + isSortable(indexPattern.timeFieldName, indexPattern) && + !hidingTimeColumn + ) { return [[indexPattern.timeFieldName, defaultSortOrder]]; } else { return []; diff --git a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx index e5a8fb2898e4b..b1fd1ae873e08 100644 --- a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx +++ b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx @@ -213,6 +213,12 @@ export class SavedSearchEmbeddable } }; + private getDefaultSort(dataView?: DataView) { + const defaultSortOrder = this.services.uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc'); + const hidingTimeColumn = this.services.uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false); + return getDefaultSort(dataView, defaultSortOrder, hidingTimeColumn); + } + private initializeSearchEmbeddableProps() { const { searchSource } = this.savedSearch; @@ -223,20 +229,14 @@ export class SavedSearchEmbeddable } if (!this.savedSearch.sort || !this.savedSearch.sort.length) { - this.savedSearch.sort = getDefaultSort( - indexPattern, - this.services.uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc') - ); + this.savedSearch.sort = this.getDefaultSort(indexPattern); } const props: SearchProps = { columns: this.savedSearch.columns, indexPattern, isLoading: false, - sort: getDefaultSort( - indexPattern, - this.services.uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc') - ), + sort: this.getDefaultSort(indexPattern), rows: [], searchDescription: this.savedSearch.description, description: this.savedSearch.description, @@ -344,10 +344,7 @@ export class SavedSearchEmbeddable const savedSearchSort = this.savedSearch.sort && this.savedSearch.sort.length ? this.savedSearch.sort - : getDefaultSort( - this.searchProps?.indexPattern, - this.services.uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc') - ); + : this.getDefaultSort(this.searchProps?.indexPattern); searchProps.sort = this.input.sort || savedSearchSort; searchProps.sharedItemTitle = this.panelTitle; searchProps.rowHeightState = this.input.rowHeight || this.savedSearch.rowHeight; From a583edcde6bc87f3e7c0ad458edc7e94cf26e022 Mon Sep 17 00:00:00 2001 From: DeDe Morton Date: Thu, 7 Apr 2022 13:27:10 -0700 Subject: [PATCH 06/42] Edit text strings and links in add logstash output UI (#129551) * Edit text strings and links in add logstash output UI * fix test to match * Add changes from review * Fix tests * Fix type * Fix I18N error Co-authored-by: Nicolas Chaulet Co-authored-by: gchaps --- packages/kbn-doc-links/src/get_doc_links.ts | 3 ++- packages/kbn-doc-links/src/types.ts | 1 + .../fleet/public/applications/fleet/app.tsx | 2 +- .../components/edit_output_flyout/index.tsx | 25 ++++++++++--------- .../output_form_validators.test.tsx | 2 +- .../output_form_validators.tsx | 2 +- .../logstash_instructions/index.tsx | 19 +++++--------- .../fleet/server/types/models/output.test.ts | 4 +-- .../fleet/server/types/models/output.ts | 4 +-- .../apis/outputs/crud.ts | 4 ++- 10 files changed, 32 insertions(+), 34 deletions(-) diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 7d58f177e0764..122486a1d0e9d 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -159,6 +159,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { }, logstash: { base: `${ELASTIC_WEBSITE_URL}guide/en/logstash/${DOC_LINK_VERSION}`, + inputElasticAgent: `${ELASTIC_WEBSITE_URL}guide/en/logstash/${DOC_LINK_VERSION}/plugins-inputs-elastic_agent.html`, }, functionbeat: { base: `${ELASTIC_WEBSITE_URL}guide/en/beats/functionbeat/${DOC_LINK_VERSION}`, @@ -579,7 +580,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { guide: `${FLEET_DOCS}index.html`, fleetServer: `${FLEET_DOCS}fleet-server.html`, fleetServerAddFleetServer: `${FLEET_DOCS}add-a-fleet-server.html`, - settings: `${FLEET_DOCS}fleet-settings.html#fleet-server-hosts-setting`, + settings: `${FLEET_DOCS}fleet-settings.html`, settingsFleetServerHostSettings: `${FLEET_DOCS}fleet-settings.html#fleet-server-hosts-setting`, settingsFleetServerProxySettings: `${KIBANA_DOCS}fleet-settings-kb.html#fleet-data-visualizer-settings`, troubleshooting: `${FLEET_DOCS}fleet-troubleshooting.html`, diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index 9a4ff1a58cf13..5e85d01c22ce2 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -141,6 +141,7 @@ export interface DocLinks { }; readonly logstash: { readonly base: string; + readonly inputElasticAgent: string; }; readonly functionbeat: { readonly base: string; diff --git a/x-pack/plugins/fleet/public/applications/fleet/app.tsx b/x-pack/plugins/fleet/public/applications/fleet/app.tsx index b5872b0a995a9..cd4ba97019766 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/app.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/app.tsx @@ -279,7 +279,7 @@ const FleetTopNav = memo( const topNavConfig: TopNavMenuData[] = [ { label: i18n.translate('xpack.fleet.appNavigation.sendFeedbackButton', { - defaultMessage: 'Send Feedback', + defaultMessage: 'Send feedback', }), iconType: 'popout', run: () => window.open(FEEDBACK_URL), diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx index 72642e68d85c1..c0b8f158f43de 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx @@ -46,7 +46,7 @@ export interface EditOutputFlyoutProps { const OUTPUT_TYPE_OPTIONS = [ { value: 'elasticsearch', text: 'Elasticsearch' }, - { value: 'logstash', text: 'Logstash (BETA)' }, + { value: 'logstash', text: 'Logstash (beta)' }, ]; export const EditOutputFlyout: React.FunctionComponent = ({ @@ -138,14 +138,15 @@ export const EditOutputFlyout: React.FunctionComponent = isLogstashOutput && ( - {i18n.translate('xpack.fleet.editOutputFlyout.learnMoreLink', { - defaultMessage: 'Learn more', - })} - + sendFeedback: ( + + + ), }} /> @@ -203,13 +204,13 @@ export const EditOutputFlyout: React.FunctionComponent = helpText={ + ), diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx index 57b8681e834bb..ea0e1541fb41c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx @@ -72,7 +72,7 @@ describe('Output form validation', () => { const res = validateLogstashHosts(['https://test.fr:5044']); expect(res).toEqual([ - { index: 0, message: 'Invalid logstash host should not start with http(s)' }, + { index: 0, message: 'Host address must begin with a domain name or IP address' }, ]); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx index 13b90fe661f61..9a95b9e834ec1 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx @@ -67,7 +67,7 @@ export function validateLogstashHosts(value: string[]) { if (val.match(/^http([s]){0,1}:\/\//)) { res.push({ message: i18n.translate('xpack.fleet.settings.outputForm.logstashHostProtocolError', { - defaultMessage: 'Invalid logstash host should not start with http(s)', + defaultMessage: 'Host address must begin with a domain name or IP address', }), index: idx, }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/logstash_instructions/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/logstash_instructions/index.tsx index 19ff2cbc61d77..1fdffd1c8a30b 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/logstash_instructions/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/logstash_instructions/index.tsx @@ -34,21 +34,21 @@ export const LogstashInstructions = () => { title={ } > <> + ), @@ -71,14 +71,14 @@ const CollapsibleCallout: React.FunctionComponent = ({ children setIsOpen(false)}> ) : ( setIsOpen(true)} fill={true}> )} @@ -191,13 +191,6 @@ const LogstashInstructionSteps = () => { }} /> - - - - ), }, diff --git a/x-pack/plugins/fleet/server/types/models/output.test.ts b/x-pack/plugins/fleet/server/types/models/output.test.ts index 8ad2b14299c73..4441630653a99 100644 --- a/x-pack/plugins/fleet/server/types/models/output.test.ts +++ b/x-pack/plugins/fleet/server/types/models/output.test.ts @@ -14,12 +14,12 @@ describe('Output model', () => { }); it('should return an error for an invalid host', () => { - expect(validateLogstashHost('!@#%&!#!@')).toMatchInlineSnapshot(`"Invalid logstash host"`); + expect(validateLogstashHost('!@#%&!#!@')).toMatchInlineSnapshot(`"Invalid Logstash host"`); }); it('should return an error for an invalid host with http scheme', () => { expect(validateLogstashHost('https://test.fr:5044')).toMatchInlineSnapshot( - `"Invalid logstash host should not start with http(s)"` + `"Host address must begin with a domain name or IP address"` ); }); }); diff --git a/x-pack/plugins/fleet/server/types/models/output.ts b/x-pack/plugins/fleet/server/types/models/output.ts index 86b2a70a318fc..2354412d073b2 100644 --- a/x-pack/plugins/fleet/server/types/models/output.ts +++ b/x-pack/plugins/fleet/server/types/models/output.ts @@ -11,7 +11,7 @@ import { outputType } from '../../../common/constants'; export function validateLogstashHost(val: string) { if (val.match(/^http([s]){0,1}:\/\//)) { - return 'Invalid logstash host should not start with http(s)'; + return 'Host address must begin with a domain name or IP address'; } try { @@ -21,7 +21,7 @@ export function validateLogstashHost(val: string) { return 'Invalid host'; } } catch (err) { - return 'Invalid logstash host'; + return 'Invalid Logstash host'; } } diff --git a/x-pack/test/fleet_api_integration/apis/outputs/crud.ts b/x-pack/test/fleet_api_integration/apis/outputs/crud.ts index 14f2e53949cfc..43a3707a21963 100644 --- a/x-pack/test/fleet_api_integration/apis/outputs/crud.ts +++ b/x-pack/test/fleet_api_integration/apis/outputs/crud.ts @@ -148,7 +148,9 @@ export default function (providerContext: FtrProviderContext) { }) .expect(400); - expect(postResponse.message).match(/Invalid logstash host should not start with http\(s\)/); + expect(postResponse.message).match( + /Host address must begin with a domain name or IP address/ + ); }); it('should toggle default output when creating a new default output ', async function () { From f218580affc56903fca1e06461a121c5c2fd3a01 Mon Sep 17 00:00:00 2001 From: Karl Godard Date: Thu, 7 Apr 2022 14:12:55 -0700 Subject: [PATCH 07/42] jump to nested entity_id fixed. verbose mode now defaults to OFF (#129783) * jump to nested entity_id fixed. verbose mode now defaults to OFF * dropping exit code UX Co-authored-by: mitodrummer --- .../public/components/process_tree/helpers.ts | 4 ++-- .../public/components/process_tree/hooks.ts | 6 +++-- .../public/components/process_tree/index.tsx | 1 + .../process_tree_node/index.test.tsx | 22 +------------------ .../components/process_tree_node/index.tsx | 7 ------ .../public/components/session_view/index.tsx | 2 +- 6 files changed, 9 insertions(+), 33 deletions(-) diff --git a/x-pack/plugins/session_view/public/components/process_tree/helpers.ts b/x-pack/plugins/session_view/public/components/process_tree/helpers.ts index 064f11c10a736..2f383271f498d 100644 --- a/x-pack/plugins/session_view/public/components/process_tree/helpers.ts +++ b/x-pack/plugins/session_view/public/components/process_tree/helpers.ts @@ -170,11 +170,11 @@ export const searchProcessTree = (processMap: ProcessMap, searchQuery: string | // b) matches the plain text search above // Returns the processMap with it's processes autoExpand bool set to true or false // process.autoExpand is read by process_tree_node to determine whether to auto expand it's child processes. -export const autoExpandProcessTree = (processMap: ProcessMap) => { +export const autoExpandProcessTree = (processMap: ProcessMap, jumpToEntityId?: string) => { for (const processId of Object.keys(processMap)) { const process = processMap[processId]; - if (process.searchMatched || process.isUserEntered()) { + if (process.searchMatched || process.isUserEntered() || jumpToEntityId === process.id) { let { parent } = process; const parentIdSet = new Set(); diff --git a/x-pack/plugins/session_view/public/components/process_tree/hooks.ts b/x-pack/plugins/session_view/public/components/process_tree/hooks.ts index 009b839f6d55c..9cbf1ca2b0f84 100644 --- a/x-pack/plugins/session_view/public/components/process_tree/hooks.ts +++ b/x-pack/plugins/session_view/public/components/process_tree/hooks.ts @@ -31,6 +31,7 @@ interface UseProcessTreeDeps { alerts: ProcessEvent[]; searchQuery?: string; updatedAlertsStatus: AlertStatusEventEntityIdMap; + jumpToEntityId?: string; } export class ProcessImpl implements Process { @@ -231,6 +232,7 @@ export const useProcessTree = ({ alerts, searchQuery, updatedAlertsStatus, + jumpToEntityId, }: UseProcessTreeDeps) => { // initialize map, as well as a placeholder for session leader process // we add a fake session leader event, sourced from wide event data. @@ -305,8 +307,8 @@ export const useProcessTree = ({ useEffect(() => { setSearchResults(searchProcessTree(processMap, searchQuery)); - autoExpandProcessTree(processMap); - }, [searchQuery, processMap]); + autoExpandProcessTree(processMap, jumpToEntityId); + }, [searchQuery, processMap, jumpToEntityId]); // set new orphans array on the session leader const sessionLeader = processMap[sessionEntityId]; diff --git a/x-pack/plugins/session_view/public/components/process_tree/index.tsx b/x-pack/plugins/session_view/public/components/process_tree/index.tsx index b8c9bbc77c9b1..158e5b8faa24a 100644 --- a/x-pack/plugins/session_view/public/components/process_tree/index.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree/index.tsx @@ -96,6 +96,7 @@ export const ProcessTree = ({ alerts, searchQuery, updatedAlertsStatus, + jumpToEntityId, }); const eventsRemaining = useMemo(() => { diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/index.test.tsx b/x-pack/plugins/session_view/public/components/process_tree_node/index.test.tsx index 81b3c184baaa4..7a3e3f775d992 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/index.test.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree_node/index.test.tsx @@ -64,7 +64,7 @@ describe('ProcessTreeNode component', () => { // expect(renderResult.queryByText(/orphaned/i)).toBeTruthy(); // }); - it('renders Exec icon and exit code for executed process', async () => { + it('renders Exec icon', async () => { const executedProcessMock: typeof processMock = { ...processMock, hasExec: () => true, @@ -75,26 +75,6 @@ describe('ProcessTreeNode component', () => { ); expect(renderResult.queryByTestId('sessionView:processTreeNodeExecIcon')).toBeTruthy(); - expect(renderResult.queryByTestId('sessionView:processTreeNodeExitCode')).toBeTruthy(); - }); - - it('does not render exit code if it does not exist', async () => { - const processWithoutExitCode: typeof processMock = { - ...processMock, - hasExec: () => true, - getDetails: () => ({ - ...processMock.getDetails(), - process: { - ...processMock.getDetails().process, - exit_code: undefined, - }, - }), - }; - - renderResult = mockedContext.render( - - ); - expect(renderResult.queryByTestId('sessionView:processTreeNodeExitCode')).toBeFalsy(); }); it('calls onChangeJumpToEventVisibility with isVisible false if jumpToEvent is not visible', async () => { diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx b/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx index b835d1d0e035d..6e5992dfa7620 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx @@ -209,7 +209,6 @@ export function ProcessTreeNode({ tty, parent, working_directory: workingDirectory, - exit_code: exitCode, start, } = processDetails.process; @@ -256,12 +255,6 @@ export function ProcessTreeNode({ {dataOrDash(workingDirectory)}  {dataOrDash(args?.[0])}{' '} {args?.slice(1).join(' ')} - {exitCode !== undefined && ( - - {' '} - [exit_code: {exitCode}] - - )} {timeStampOn && ( diff --git a/x-pack/plugins/session_view/public/components/session_view/index.tsx b/x-pack/plugins/session_view/public/components/session_view/index.tsx index 6ba0901e973bf..1e41878814697 100644 --- a/x-pack/plugins/session_view/public/components/session_view/index.tsx +++ b/x-pack/plugins/session_view/public/components/session_view/index.tsx @@ -62,7 +62,7 @@ export const SessionView = ({ LOCAL_STORAGE_DISPLAY_OPTIONS_KEY, { timestamp: true, - verboseMode: true, + verboseMode: false, } ); const [fetchAlertStatus, setFetchAlertStatus] = useState([]); From e076067b09a6df0254cf128d136931202a4fd907 Mon Sep 17 00:00:00 2001 From: Lee Drengenberg Date: Thu, 7 Apr 2022 16:16:38 -0500 Subject: [PATCH 08/42] unskip test kibana/test/functional/apps/discover/_saved_queries.ts (#128678) * unskip tes _saved_queries * remove sleep call from debugging, remove commented-out code * add submitQuery and wait for correct counts at each step * more debugging checks * re-select logstash-* data view Flaky test runner failed 6/100 times at this point so try setting it here again. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../apps/discover/_saved_queries.ts | 68 +++++++++++-------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/test/functional/apps/discover/_saved_queries.ts b/test/functional/apps/discover/_saved_queries.ts index 0e821b7006bf9..fd49a03413321 100644 --- a/test/functional/apps/discover/_saved_queries.ts +++ b/test/functional/apps/discover/_saved_queries.ts @@ -25,22 +25,39 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { defaultIndex: 'logstash-*', }; + const from = 'Sep 20, 2015 @ 08:00:00.000'; + const to = 'Sep 21, 2015 @ 08:00:00.000'; + const setUpQueriesWithFilters = async () => { + await kibanaServer.savedObjects.clean({ types: ['search', 'query'] }); // set up a query with filters and a time filter log.debug('set up a query with filters to save'); - const from = 'Sep 20, 2015 @ 08:00:00.000'; - const to = 'Sep 21, 2015 @ 08:00:00.000'; await PageObjects.common.setTime({ from, to }); await PageObjects.common.navigateToApp('discover'); + await PageObjects.discover.selectIndexPattern('logstash-*'); + await retry.try(async function tryingForTime() { + const hitCount = await PageObjects.discover.getHitCount(); + expect(hitCount).to.be('4,731'); + }); + await filterBar.addFilter('extension.raw', 'is one of', 'jpg'); + await retry.try(async function tryingForTime() { + const hitCount = await PageObjects.discover.getHitCount(); + expect(hitCount).to.be('3,029'); + }); + await queryBar.setQuery('response:200'); + await queryBar.submitQuery(); + await retry.try(async function tryingForTime() { + const hitCount = await PageObjects.discover.getHitCount(); + expect(hitCount).to.be('2,792'); + }); }; - // Failing: See https://github.com/elastic/kibana/issues/124990 - describe.skip('saved queries saved objects', function describeIndexTests() { + describe('saved queries saved objects', function describeIndexTests() { before(async function () { log.debug('load kibana index with default index pattern'); - await kibanaServer.savedObjects.clean({ types: ['search', 'index-pattern'] }); + await kibanaServer.savedObjects.clean({ types: ['search', 'index-pattern', 'query'] }); await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover.json'); await kibanaServer.importExport.load( @@ -52,12 +69,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.uiSettings.replace(defaultSettings); log.debug('discover'); await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setDefaultAbsoluteRange(); }); after(async () => { await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/date_nested'); + await kibanaServer.savedObjects.clean({ types: ['search', 'index-pattern', 'query'] }); + await kibanaServer.savedObjects.clean({ types: ['search', 'query'] }); await esArchiver.unload('test/functional/fixtures/es_archiver/date_nested'); await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); await PageObjects.common.unsetTime(); @@ -90,6 +108,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await queryBar.getQueryString()).to.eql(''); await PageObjects.discover.selectIndexPattern('logstash-*'); + const currentDataView = await PageObjects.discover.getCurrentlySelectedDataView(); + expect(currentDataView).to.be('logstash-*'); + await retry.try(async function tryingForTime() { + const hitCount = await PageObjects.discover.getHitCount(); + expect(hitCount).to.be('4,731'); + }); expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(false); expect(await queryBar.getQueryString()).to.eql(''); @@ -99,8 +123,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/124986 - describe.skip('saved query management component functionality', function () { + describe('saved query management component functionality', function () { before(async () => await setUpQueriesWithFilters()); it('should show the saved query management component when there are no saved queries', async () => { @@ -118,6 +141,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { true, true ); + await savedQueryManagementComponent.savedQueryExistOrFail('OkResponse'); await savedQueryManagementComponent.savedQueryTextExist('response:200'); }); @@ -128,20 +152,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await savedQueryManagementComponent.loadSavedQuery('OkResponse'); const timePickerValues = await PageObjects.timePicker.getTimeConfigAsAbsoluteTimes(); expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(true); - expect(timePickerValues.start).to.not.eql(PageObjects.timePicker.defaultStartTime); - expect(timePickerValues.end).to.not.eql(PageObjects.timePicker.defaultEndTime); + expect(timePickerValues.start).to.eql(from); + expect(timePickerValues.end).to.eql(to); }); it('preserves the currently loaded query when the page is reloaded', async () => { await browser.refresh(); const timePickerValues = await PageObjects.timePicker.getTimeConfigAsAbsoluteTimes(); expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(true); - expect(timePickerValues.start).to.not.eql(PageObjects.timePicker.defaultStartTime); - expect(timePickerValues.end).to.not.eql(PageObjects.timePicker.defaultEndTime); - await retry.waitFor( - 'the right hit count', - async () => (await PageObjects.discover.getHitCount()) === '2,792' - ); + expect(timePickerValues.start).to.eql(from); + expect(timePickerValues.end).to.eql(to); + await retry.waitForWithTimeout('the right hit count', 65000, async () => { + const hitCount = await PageObjects.discover.getHitCount(); + log.debug(`Found hit count is ${hitCount}. Looking for 2,792.`); + return hitCount === '2,792'; + }); expect(await savedQueryManagementComponent.getCurrentlyLoadedQueryID()).to.be('OkResponse'); }); @@ -172,17 +197,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('does not allow saving a query with a non-unique name', async () => { - // this check allows this test to run stand alone, also should fix occacional flakiness - const savedQueryExists = await savedQueryManagementComponent.savedQueryExist('OkResponse'); - if (!savedQueryExists) { - await savedQueryManagementComponent.saveNewQuery( - 'OkResponse', - '200 responses for .jpg over 24 hours', - true, - true - ); - await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); - } await savedQueryManagementComponent.saveNewQueryWithNameError('OkResponse'); }); From c72a954c0945e2d90069726c70c782c91c828cea Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Thu, 7 Apr 2022 17:53:55 -0400 Subject: [PATCH 09/42] [Dashboard] Explicitly Hide Panel Titles Migration (#129540) * add dashboard migration to explicitly hide the titles on panels whose titles were hidden in 7.9 --- .../dashboard_migrations.test.ts | 107 ++++++++++++++++++ .../saved_objects/dashboard_migrations.ts | 58 ++++++++-- 2 files changed, 158 insertions(+), 7 deletions(-) diff --git a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts index 6e1a6ccf1c86e..9a2afecf0aa96 100644 --- a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts +++ b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts @@ -473,6 +473,113 @@ describe('dashboard', () => { }); }); + describe('7.10.0 - hidden panel titles', () => { + const migration = migrations['7.17.3']; + const doc: DashboardDoc730ToLatest = { + attributes: { + description: '', + kibanaSavedObjectMeta: { + searchSourceJSON: '', + }, + optionsJSON: '', + panelsJSON: `[ + {"version":"7.9.3","gridData":{"h":15,"i":"ad30af17-3897-4988-8dd9-1d4ccec60324","w":24,"x":0,"y":0},"panelIndex":"ad30af17-3897-4988-8dd9-1d4ccec60324","embeddableConfig":{"title":"Custom title"},"panelRefName":"panel_0"}, + {"version":"7.9.3","gridData":{"h":15,"i":"1132db5f-6fe9-4762-8199-3017bb6ed936","w":24,"x":24,"y":0},"panelIndex":"1132db5f-6fe9-4762-8199-3017bb6ed936","embeddableConfig":{"title":""},"panelRefName":"panel_1"}, + {"version":"7.9.3","gridData":{"h":15,"i":"9f0cc291-de38-42f4-b565-e13678cb5a88","w":24,"x":0,"y":15},"panelIndex":"9f0cc291-de38-42f4-b565-e13678cb5a88","embeddableConfig":{"title":""},"panelRefName":"panel_2"}, + {"version":"7.9.3","gridData":{"h":15,"i":"94b09a97-8775-4886-be22-c1ad53a7e361","w":24,"x":24,"y":15},"panelIndex":"94b09a97-8775-4886-be22-c1ad53a7e361","embeddableConfig":{},"panelRefName":"panel_3"} + ]`, + timeRestore: false, + title: 'Dashboard with blank titles', + version: 1, + }, + id: '376e6260-1f5e-11eb-91aa-7b6d5f8a61d6', + references: [], + type: 'dashboard', + }; + + test('all panels with explicitly set titles are left alone', () => { + const newDoc = migration(doc, contextMock); + const newPanels = JSON.parse(newDoc.attributes.panelsJSON); + expect(newPanels[0]).toMatchInlineSnapshot(` + Object { + "embeddableConfig": Object {}, + "gridData": Object { + "h": 15, + "i": "ad30af17-3897-4988-8dd9-1d4ccec60324", + "w": 24, + "x": 0, + "y": 0, + }, + "panelIndex": "ad30af17-3897-4988-8dd9-1d4ccec60324", + "panelRefName": "panel_0", + "title": "Custom title", + "version": "7.9.3", + } + `); + }); + + test('all panels with blank string titles are set to hidden', () => { + const newDoc = migration(doc, contextMock); + const newPanels = JSON.parse(newDoc.attributes.panelsJSON); + expect(newPanels[1]).toMatchInlineSnapshot(` + Object { + "embeddableConfig": Object { + "hidePanelTitles": true, + }, + "gridData": Object { + "h": 15, + "i": "1132db5f-6fe9-4762-8199-3017bb6ed936", + "w": 24, + "x": 24, + "y": 0, + }, + "panelIndex": "1132db5f-6fe9-4762-8199-3017bb6ed936", + "panelRefName": "panel_1", + "title": "", + "version": "7.9.3", + } + `); + expect(newPanels[2]).toMatchInlineSnapshot(` + Object { + "embeddableConfig": Object { + "hidePanelTitles": true, + }, + "gridData": Object { + "h": 15, + "i": "9f0cc291-de38-42f4-b565-e13678cb5a88", + "w": 24, + "x": 0, + "y": 15, + }, + "panelIndex": "9f0cc291-de38-42f4-b565-e13678cb5a88", + "panelRefName": "panel_2", + "title": "", + "version": "7.9.3", + } + `); + }); + + test('all panels with undefined titles are left alone', () => { + const newDoc = migration(doc, contextMock); + const newPanels = JSON.parse(newDoc.attributes.panelsJSON); + expect(newPanels[3]).toMatchInlineSnapshot(` + Object { + "embeddableConfig": Object {}, + "gridData": Object { + "h": 15, + "i": "94b09a97-8775-4886-be22-c1ad53a7e361", + "w": 24, + "x": 24, + "y": 15, + }, + "panelIndex": "94b09a97-8775-4886-be22-c1ad53a7e361", + "panelRefName": "panel_3", + "version": "7.9.3", + } + `); + }); + }); + describe('7.11.0 - embeddable persistable state extraction', () => { const migration = migrations['7.11.0']; const doc: DashboardDoc730ToLatest = { diff --git a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts index bd3051fc5a257..d50c199c7f0d4 100644 --- a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts +++ b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts @@ -164,6 +164,56 @@ type ValueOrReferenceInput = SavedObjectEmbeddableInput & { savedVis?: Serializable; }; +/** + * Before 7.10, hidden panel titles were stored as a blank string on the title attribute. In 7.10, this was replaced + * with a usage of the existing hidePanelTitles key. Even though blank string titles still technically work + * in versions > 7.10, they are less explicit than using the hidePanelTitles key. This migration transforms all + * blank string titled panels to panels with the titles explicitly hidden. + */ +export const migrateExplicitlyHiddenTitles: SavedObjectMigrationFn = (doc) => { + const { attributes } = doc; + + // Skip if panelsJSON is missing + if (typeof attributes?.panelsJSON !== 'string') return doc; + + try { + const panels = JSON.parse(attributes.panelsJSON) as SavedDashboardPanel[]; + // Same here, prevent failing saved object import if ever panels aren't an array. + if (!Array.isArray(panels)) return doc; + + const newPanels: SavedDashboardPanel[] = []; + panels.forEach((panel) => { + // Convert each panel into the dashboard panel state + const originalPanelState = + convertSavedDashboardPanelToPanelState(panel); + newPanels.push( + convertPanelStateToSavedDashboardPanel( + { + ...originalPanelState, + explicitInput: { + ...originalPanelState.explicitInput, + ...(originalPanelState.explicitInput.title === '' && + !originalPanelState.explicitInput.hidePanelTitles + ? { hidePanelTitles: true } + : {}), + }, + }, + panel.version + ) + ); + }); + return { + ...doc, + attributes: { + ...attributes, + panelsJSON: JSON.stringify(newPanels), + }, + }; + } catch { + return doc; + } +}; + // Runs the embeddable migrations on each panel const migrateByValuePanels = (migrate: MigrateFunction, version: string): SavedObjectMigrationFn => @@ -258,14 +308,8 @@ export const createDashboardSavedObjectTypeMigrations = ( '7.3.0': flow(migrations730), '7.9.3': flow(migrateMatchAllQuery), '7.11.0': flow(createExtractPanelReferencesMigration(deps)), - - /** - * Any dashboard saved object migrations that come after this point will have to be wary of - * potentially overwriting embeddable migrations. An example of how to mitigate this follows: - */ - // '7.x': flow(yourNewMigrationFunction, embeddableMigrations['7.x'] ?? identity), - '7.14.0': flow(replaceIndexPatternReference), + '7.17.3': flow(migrateExplicitlyHiddenTitles), }; return mergeMigrationFunctionMaps(dashboardMigrations, embeddableMigrations); From 78906a3e79c2a666993198e1610f4c31465f8960 Mon Sep 17 00:00:00 2001 From: Jack Date: Thu, 7 Apr 2022 18:12:50 -0400 Subject: [PATCH 10/42] =?UTF-8?q?[Security=20Solution]=20[Session=20View]?= =?UTF-8?q?=20[8.2]=20[8.3]=20Separate=20session=20view=20with=20its=20nav?= =?UTF-8?q?igation=20and=20fix=20rerender=20when=20toggl=E2=80=A6=20(#1297?= =?UTF-8?q?73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Separate session view with its navigation and fix rerender when toggle full screen * Fix use_session_view tests * Fix PR sugguestions * Fix CI checks * Add background color to session view detail panel to avoid leakage --- .../common/components/events_viewer/index.tsx | 11 ++- .../components/graph_overlay/index.tsx | 56 ++++++----- .../timeline/graph_tab_content/index.tsx | 8 +- .../timeline/session_tab_content/index.tsx | 7 +- .../use_session_view.test.tsx | 72 ++++++++++++++- .../session_tab_content/use_session_view.tsx | 92 +++++++++++-------- .../session_view_detail_panel/index.tsx | 2 +- .../session_view_detail_panel/styles.ts | 9 +- 8 files changed, 184 insertions(+), 73 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx index b3948681c491c..7dfa75d6daaa2 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -32,7 +32,10 @@ import { useFieldBrowserOptions, FieldEditorActions, } from '../../../timelines/components/fields_browser'; -import { useSessionView } from '../../../timelines/components/timeline/session_tab_content/use_session_view'; +import { + useSessionViewNavigation, + useSessionView, +} from '../../../timelines/components/timeline/session_tab_content/use_session_view'; const EMPTY_CONTROL_COLUMNS: ControlColumnProps[] = []; @@ -157,7 +160,11 @@ const StatefulEventsViewerComponent: React.FC = ({ const globalFilters = useMemo(() => [...filters, ...(pageFilters ?? [])], [filters, pageFilters]); const trailingControlColumns: ControlColumnProps[] = EMPTY_CONTROL_COLUMNS; - const { DetailsPanel, SessionView, Navigation } = useSessionView({ + const { Navigation } = useSessionViewNavigation({ + timelineId: id, + }); + + const { DetailsPanel, SessionView } = useSessionView({ entityType, timelineId: id, }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx index e5aef07754f48..2087bb1a5e511 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx @@ -5,10 +5,11 @@ * 2.0. */ -import React, { useMemo, useEffect } from 'react'; +import React, { useMemo, useEffect, useRef, useLayoutEffect } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiLoadingSpinner } from '@elastic/eui'; +import { euiThemeVars } from '@kbn/ui-theme'; import { useDispatch } from 'react-redux'; -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; import { useGlobalFullScreen, useTimelineFullScreen, @@ -29,20 +30,28 @@ import { SourcererScopeName } from '../../../common/store/sourcerer/model'; import { useSourcererDataView } from '../../../common/containers/sourcerer'; import { sourcererSelectors } from '../../../common/store'; -const OverlayContainer = styled.div` +const OverlayStyle = css` display: flex; flex-direction: column; flex: 1; width: 100%; `; -const FullScreenOverlayContainer = styled.div` +const OverlayContainer = styled.div` + ${OverlayStyle} +`; + +const FullScreenOverlayStyles = css` position: fixed; top: 0; bottom: 0; left: 0; right: 0; - z-index: ${(props) => props.theme.eui.euiZLevel3}; + z-index: ${euiThemeVars.euiZLevel3}; +`; + +const FullScreenOverlayContainer = styled.div` + ${FullScreenOverlayStyles} `; const StyledResolver = styled(Resolver)` @@ -134,26 +143,25 @@ const GraphOverlayComponent: React.FC = ({ [defaultDataView.patternList, isInTimeline, timelinePatterns] ); - if (!isInTimeline && sessionViewConfig !== null) { - if (fullScreen) { - return ( - - - {Navigation} - {SessionView} - - - ); - } else { - return ( - - - {Navigation} - {SessionView} - - - ); + const sessionContainerRef = useRef(null); + + useLayoutEffect(() => { + if (fullScreen && sessionContainerRef.current) { + sessionContainerRef.current.setAttribute('style', FullScreenOverlayStyles.join('')); + } else if (sessionContainerRef.current) { + sessionContainerRef.current.setAttribute('style', OverlayStyle.join('')); } + }, [fullScreen]); + + if (!isInTimeline && sessionViewConfig !== null) { + return ( + + + {Navigation} + {SessionView} + + + ); } else if (fullScreen && !isInTimeline) { return ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/graph_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/graph_tab_content/index.tsx index adedda3a5f253..b835c87308e15 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/graph_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/graph_tab_content/index.tsx @@ -12,7 +12,7 @@ import { timelineSelectors } from '../../../store/timeline'; import { useShallowEqualSelector } from '../../../../common/hooks/use_selector'; import { TimelineId } from '../../../../../common/types/timeline'; import { GraphOverlay } from '../../graph_overlay'; -import { useSessionView } from '../session_tab_content/use_session_view'; +import { useSessionViewNavigation, useSessionView } from '../session_tab_content/use_session_view'; interface GraphTabContentProps { timelineId: TimelineId; @@ -35,7 +35,11 @@ const GraphTabContentComponent: React.FC = ({ timelineId } (state) => getTimeline(state, timelineId)?.graphEventId ); - const { shouldShowDetailsPanel, DetailsPanel, Navigation, SessionView } = useSessionView({ + const { Navigation } = useSessionViewNavigation({ + timelineId, + }); + + const { shouldShowDetailsPanel, DetailsPanel, SessionView } = useSessionView({ timelineId, }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/index.tsx index 8035dda95081f..6d359e2c8403d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/index.tsx @@ -9,7 +9,7 @@ import React, { useState, useCallback } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import styled from 'styled-components'; import { TimelineId } from '../../../../../common/types/timeline'; -import { useSessionView } from './use_session_view'; +import { useSessionViewNavigation, useSessionView } from './use_session_view'; const FlexItemWithMargin = styled(EuiFlexItem)` ${({ theme }) => `margin: 0 ${theme.eui.euiSizeM};`} @@ -43,7 +43,10 @@ const SessionTabContent: React.FC = ({ timelineId }) => { setHeight(node.getBoundingClientRect().height); } }, []); - const { SessionView, shouldShowDetailsPanel, DetailsPanel, Navigation } = useSessionView({ + const { Navigation } = useSessionViewNavigation({ + timelineId, + }); + const { SessionView, shouldShowDetailsPanel, DetailsPanel } = useSessionView({ timelineId, height, }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/use_session_view.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/use_session_view.test.tsx index 3e1f087a0d726..12fedad3b225e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/use_session_view.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/use_session_view.test.tsx @@ -17,7 +17,7 @@ import { useTimelineFullScreen, useGlobalFullScreen, } from '../../../../common/containers/use_full_screen'; -import { useSessionView } from './use_session_view'; +import { useSessionView, useSessionViewNavigation } from './use_session_view'; const mockDispatch = jest.fn(); jest.mock('../../../../common/hooks/use_selector'); @@ -139,7 +139,7 @@ describe('useSessionView with active timeline and a session id and graph event i const testProps = { timelineId: TimelineId.active, }; - return useSessionView(testProps); + return useSessionViewNavigation(testProps); }, { wrapper: Wrapper } ); @@ -194,7 +194,7 @@ describe('useSessionView with active timeline and a session id and graph event i const testProps = { timelineId: TimelineId.hostsPageEvents, }; - return useSessionView(testProps); + return useSessionViewNavigation(testProps); }, { wrapper: Wrapper } ); @@ -203,4 +203,70 @@ describe('useSessionView with active timeline and a session id and graph event i expect(renderResult.getByText('Close analyzer')).toBeTruthy(); }); }); + + describe('useSessionView and useSessionViewNavigation should handle separate parts', () => { + beforeEach(() => { + setTimelineFullScreen = jest.fn(); + setGlobalFullScreen = jest.fn(); + (useTimelineFullScreen as jest.Mock).mockImplementation(() => ({ + setTimelineFullScreen, + })); + (useGlobalFullScreen as jest.Mock).mockImplementation(() => ({ + setGlobalFullScreen, + })); + (useDeepEqualSelector as jest.Mock).mockImplementation(() => { + return { + ...mockTimelineModel, + activeTab: TimelineTabs.session, + graphEventId: 'current-graph-event-id', + sessionViewConfig: { + sessionEntityId: 'test', + }, + show: true, + }; + }); + }); + afterEach(() => { + (useDeepEqualSelector as jest.Mock).mockClear(); + }); + it('useSessionView should handle session view and details panel', () => { + const { result } = renderHook( + () => { + const testProps = { + timelineId: TimelineId.active, + }; + return useSessionView(testProps); + }, + { wrapper: Wrapper } + ); + expect(kibana.services.sessionView.getSessionView).toHaveBeenCalled(); + + expect(result.current).toHaveProperty('openDetailsPanel'); + expect(result.current).toHaveProperty('shouldShowDetailsPanel'); + expect(result.current).toHaveProperty('SessionView'); + expect(result.current).toHaveProperty('DetailsPanel'); + + expect(result.current).not.toHaveProperty('Navigation'); + expect(result.current).not.toHaveProperty('onCloseOverlay'); + }); + + it('useSessionViewNavigation should handle Navigation component and on close callback', () => { + const { result } = renderHook( + () => { + const testProps = { + timelineId: TimelineId.hostsPageEvents, + }; + return useSessionViewNavigation(testProps); + }, + { wrapper: Wrapper } + ); + expect(result.current).toHaveProperty('Navigation'); + expect(result.current).toHaveProperty('onCloseOverlay'); + + expect(result.current).not.toHaveProperty('openDetailsPanel'); + expect(result.current).not.toHaveProperty('shouldShowDetailsPanel'); + expect(result.current).not.toHaveProperty('SessionView'); + expect(result.current).not.toHaveProperty('DetailsPanel'); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/use_session_view.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/use_session_view.tsx index 370509e4c88dc..693429ac9fc8c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/use_session_view.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/use_session_view.tsx @@ -46,6 +46,7 @@ export interface SessionViewConfig { const FullScreenButtonIcon = styled(EuiButtonIcon)` margin: 4px 0 4px 0; `; + interface NavigationProps { fullScreen: boolean; globalFullScreen: boolean; @@ -106,16 +107,7 @@ NavigationComponent.displayName = 'NavigationComponent'; const Navigation = React.memo(NavigationComponent); -export const useSessionView = ({ - timelineId, - entityType, - height, -}: { - timelineId: TimelineId; - entityType?: EntityType; - height?: number; -}) => { - const { sessionView } = useKibana().services; +export const useSessionViewNavigation = ({ timelineId }: { timelineId: TimelineId }) => { const dispatch = useDispatch(); const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); @@ -187,6 +179,60 @@ export const useSessionView = ({ setGlobalFullScreen, globalFullScreen, ]); + const navigation = useMemo(() => { + return ( + + ); + }, [ + fullScreen, + globalFullScreen, + activeTab, + graphEventId, + onCloseOverlay, + timelineFullScreen, + timelineId, + toggleFullScreen, + ]); + + return { + onCloseOverlay, + Navigation: navigation, + }; +}; + +export const useSessionView = ({ + timelineId, + entityType, + height, +}: { + timelineId: TimelineId; + entityType?: EntityType; + height?: number; +}) => { + const { sessionView } = useKibana().services; + const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); + + const { globalFullScreen } = useGlobalFullScreen(); + const { timelineFullScreen } = useTimelineFullScreen(); + + const { sessionViewConfig, activeTab } = useDeepEqualSelector( + (state) => getTimeline(state, timelineId) ?? timelineDefaults + ); + + const fullScreen = useMemo( + () => isFullScreen({ globalFullScreen, timelineId, timelineFullScreen }), + [globalFullScreen, timelineId, timelineFullScreen] + ); + const sourcererScope = useMemo(() => { if (timelineId === TimelineId.active) { return SourcererScopeName.timeline; @@ -217,36 +263,10 @@ export const useSessionView = ({ : null; }, [fullScreen, openDetailsPanel, sessionView, sessionViewConfig, height]); - const navigation = useMemo(() => { - return ( - - ); - }, [ - fullScreen, - globalFullScreen, - activeTab, - graphEventId, - onCloseOverlay, - timelineFullScreen, - timelineId, - toggleFullScreen, - ]); - return { - onCloseOverlay, openDetailsPanel, shouldShowDetailsPanel, SessionView: sessionViewComponent, DetailsPanel, - Navigation: navigation, }; }; diff --git a/x-pack/plugins/session_view/public/components/session_view_detail_panel/index.tsx b/x-pack/plugins/session_view/public/components/session_view_detail_panel/index.tsx index c2e8eec173422..86cfacfb72d06 100644 --- a/x-pack/plugins/session_view/public/components/session_view_detail_panel/index.tsx +++ b/x-pack/plugins/session_view/public/components/session_view_detail_panel/index.tsx @@ -105,7 +105,7 @@ export const SessionViewDetailPanel = ({ const styles = useStyles(); return ( -
+
{tabs.map((tab, index) => ( { const { euiTheme } = useEuiTheme(); const cached = useMemo(() => { - const detailsPanelLeftBorder: CSSObject = { - borderLeft: euiTheme.border.thin, + const { border, colors } = euiTheme; + + const detailsPanel: CSSObject = { + borderLeft: border.thin, + backgroundColor: colors.emptyShade, }; return { - detailsPanelLeftBorder, + detailsPanel, }; }, [euiTheme]); From da912675176a300fcd616078016f2a20832055c6 Mon Sep 17 00:00:00 2001 From: Karl Godard Date: Thu, 7 Apr 2022 15:16:14 -0700 Subject: [PATCH 11/42] fixes alignment issue with timestamp when process is long and wraps (#129807) Co-authored-by: mitodrummer --- .../public/components/process_tree_node/index.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx b/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx index 6e5992dfa7620..208a0c6fbfd5b 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx @@ -248,6 +248,11 @@ export function ProcessTreeNode({ ) : ( + {timeStampOn && ( + + {timeStampsNormal} + + )} {' '} @@ -256,11 +261,6 @@ export function ProcessTreeNode({ {dataOrDash(args?.[0])}{' '} {args?.slice(1).join(' ')} - {timeStampOn && ( - - {timeStampsNormal} - - )} )} From b1a4a706246b2d9306d5e0705615c1e487206813 Mon Sep 17 00:00:00 2001 From: Jack Date: Thu, 7 Apr 2022 18:55:03 -0400 Subject: [PATCH 12/42] Set timeline session view flex item width to 100% (#129813) --- .../timelines/components/timeline/session_tab_content/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/index.tsx index 6d359e2c8403d..324c13d320f45 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/session_tab_content/index.tsx @@ -12,6 +12,7 @@ import { TimelineId } from '../../../../../common/types/timeline'; import { useSessionViewNavigation, useSessionView } from './use_session_view'; const FlexItemWithMargin = styled(EuiFlexItem)` + width: 100%; ${({ theme }) => `margin: 0 ${theme.eui.euiSizeM};`} `; From 699c17afec518a5724d3351b5fe74b554faa5964 Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Thu, 7 Apr 2022 19:23:14 -0400 Subject: [PATCH 13/42] moment 2.28.0 -> 2.29.2 (#129814) --- package.json | 2 +- .../pages/detection_engine/rules/helpers.test.tsx | 2 -- .../server/lib/detection_engine/signals/utils.test.ts | 1 - yarn.lock | 8 ++++---- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 05dcd0f6691dd..2f159c19ba009 100644 --- a/package.json +++ b/package.json @@ -310,7 +310,7 @@ "mime-types": "^2.1.27", "mini-css-extract-plugin": "1.1.0", "minimatch": "^3.1.2", - "moment": "^2.24.0", + "moment": "^2.29.2", "moment-duration-format": "^2.3.2", "moment-timezone": "^0.5.27", "monaco-editor": "^0.22.3", diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx index e5381757670ea..166395673e726 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx @@ -35,8 +35,6 @@ import { import { getThreatMock } from '../../../../../common/detection_engine/schemas/types/threat.mock'; describe('rule helpers', () => { - // @ts-expect-error 4.3.5 upgrade - likely requires moment upgrade - // https://github.com/elastic/kibana/issues/120236 moment.suppressDeprecationWarnings = true; describe('getStepsData', () => { test('returns object with about, define, schedule and actions step properties formatted', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts index 02ed86b65e878..3b0d5d17b4445 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts @@ -18,7 +18,6 @@ import { RuleExecutionStatus } from '../../../../common/detection_engine/schemas import { getListArrayMock } from '../../../../common/detection_engine/schemas/types/lists.mock'; import { getExceptionListItemSchemaMock } from '../../../../../lists/common/schemas/response/exception_list_item_schema.mock'; -// @ts-expect-error 4.3.5 upgrade - likely requires moment upgrade moment.suppressDeprecationWarnings = true; import { diff --git a/yarn.lock b/yarn.lock index db4b0b07adf11..43a4fd6a2d148 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20399,10 +20399,10 @@ moment-timezone@^0.5.27: dependencies: moment ">= 2.9.0" -"moment@>= 2.9.0", moment@>=1.6.0, moment@>=2.14.0, moment@^2.10.6, moment@^2.24.0: - version "2.28.0" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.28.0.tgz#cdfe73ce01327cee6537b0fafac2e0f21a237d75" - integrity sha512-Z5KOjYmnHyd/ukynmFd/WwyXHd7L4J9vTI/nn5Ap9AVUgaAE15VvQ9MOGmJJygEUklupqIrFnor/tjTwRU+tQw== +"moment@>= 2.9.0", moment@>=1.6.0, moment@>=2.14.0, moment@^2.10.6, moment@^2.29.2: + version "2.29.2" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.2.tgz#00910c60b20843bcba52d37d58c628b47b1f20e4" + integrity sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg== monaco-editor@*, monaco-editor@^0.22.3: version "0.22.3" From 3d163ec01175e16a1d05fa2edbceee4330cdedbc Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Thu, 7 Apr 2022 21:43:40 -0400 Subject: [PATCH 14/42] Bump another dependency (#129833) --- yarn.lock | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 43a4fd6a2d148..ea4324cd13607 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8419,12 +8419,7 @@ async@^2.1.4, async@^2.6.2: dependencies: lodash "^4.17.14" -async@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" - integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== - -async@^3.2.3: +async@^3.2.0, async@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9" integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g== From 1841563e55d8681d9dd48c897a9f022df1bc9938 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Fri, 8 Apr 2022 17:47:28 +0500 Subject: [PATCH 15/42] [Step 2] [Charts Expressions] update imports to '@kbn/coloring' (#129722) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../expression_gauge/common/index.ts | 3 - .../common/types/expression_functions.ts | 4 +- .../common/types/expression_renderers.ts | 25 +-- .../components/gauge_component.test.tsx | 2 +- .../public/components/gauge_component.tsx | 3 +- .../public/components/utils/accessors.ts | 3 +- .../public/services/palette_service.ts | 2 +- .../expression_heatmap/common/index.ts | 3 - .../common/types/expression_functions.ts | 3 +- .../common/types/expression_renderers.ts | 23 +- .../public/components/heatmap_component.tsx | 10 +- .../public/components/helpers.test.ts | 206 ------------------ .../public/components/helpers.ts | 119 +--------- .../expression_heatmap/public/constants.ts | 19 +- .../public/services/palette_service.ts | 3 +- .../common/types/expression_functions.ts | 3 +- .../public/services/palette_service.ts | 2 +- .../common/types/expression_renderers.ts | 2 +- .../public/__mocks__/palettes.ts | 2 +- .../components/partition_vis_component.tsx | 3 +- .../public/utils/layers/get_color.test.ts | 2 +- .../public/utils/layers/get_color.ts | 9 +- .../public/utils/layers/get_layers.test.ts | 2 +- .../public/utils/layers/get_layers.ts | 2 +- .../common/types/expression_functions.ts | 2 +- .../public/__mocks__/palettes.ts | 2 +- .../public/components/tagcloud_component.tsx | 2 +- .../expression_xy/common/__mocks__/index.ts | 2 +- .../common/types/expression_functions.ts | 2 +- .../public/components/xy_chart.tsx | 3 +- .../xy_chart_renderer.tsx | 3 +- 31 files changed, 65 insertions(+), 406 deletions(-) diff --git a/src/plugins/chart_expressions/expression_gauge/common/index.ts b/src/plugins/chart_expressions/expression_gauge/common/index.ts index 395aa3ed60861..24d4dc3c8d997 100755 --- a/src/plugins/chart_expressions/expression_gauge/common/index.ts +++ b/src/plugins/chart_expressions/expression_gauge/common/index.ts @@ -14,9 +14,6 @@ export type { GaugeExpressionProps, FormatFactory, GaugeRenderProps, - CustomPaletteParams, - ColorStop, - RequiredPaletteParamTypes, GaugeArguments, GaugeShape, GaugeLabelMajorMode, diff --git a/src/plugins/chart_expressions/expression_gauge/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_gauge/common/types/expression_functions.ts index b2696acda6c7d..b090dc5346146 100644 --- a/src/plugins/chart_expressions/expression_gauge/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_gauge/common/types/expression_functions.ts @@ -7,13 +7,14 @@ */ import { $Values } from '@kbn/utility-types'; +import type { PaletteOutput, CustomPaletteParams } from '@kbn/coloring'; import { Datatable, ExpressionFunctionDefinition, ExpressionValueRender, } from '../../../../expressions'; import { ExpressionValueVisDimension } from '../../../../visualizations/public'; -import { CustomPaletteState, PaletteOutput } from '../../../../charts/common'; +import { CustomPaletteState } from '../../../../charts/common'; import { EXPRESSION_GAUGE_NAME, GAUGE_FUNCTION_RENDERER_NAME, @@ -23,7 +24,6 @@ import { GaugeColorModes, GaugeCentralMajorModes, } from '../constants'; -import { CustomPaletteParams } from '.'; export type GaugeColorMode = $Values; export type GaugeShape = $Values; diff --git a/src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts index 4c2133e8572f8..2f1560550690b 100644 --- a/src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts @@ -6,8 +6,9 @@ * Side Public License, v 1. */ -import { PersistedState } from '../../../../visualizations/public'; -import type { ChartsPluginSetup, PaletteRegistry } from '../../../../charts/public'; +import type { PaletteRegistry } from '@kbn/coloring'; +import type { PersistedState } from '../../../../visualizations/public'; +import type { ChartsPluginSetup } from '../../../../charts/public'; import type { IFieldFormat, SerializedFieldFormat } from '../../../../field_formats/common'; import type { GaugeExpressionProps } from './expression_functions'; @@ -19,23 +20,3 @@ export type GaugeRenderProps = GaugeExpressionProps & { paletteService: PaletteRegistry; uiState: PersistedState; }; - -export interface ColorStop { - color: string; - stop: number; -} - -export interface CustomPaletteParams { - name?: string; - reverse?: boolean; - rangeType?: 'number' | 'percent'; - continuity?: 'above' | 'below' | 'all' | 'none'; - progression?: 'fixed'; - rangeMin?: number; - rangeMax?: number; - stops?: ColorStop[]; - colorStops?: ColorStop[]; - steps?: number; -} - -export type RequiredPaletteParamTypes = Required; diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx index e7e1e47ca65f2..ef21811402c2a 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx +++ b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx @@ -7,6 +7,7 @@ */ import React from 'react'; +import { ColorStop } from '@kbn/coloring'; import { chartPluginMock } from '../../../../charts/public/mocks'; import { fieldFormatsServiceMock } from '../../../../field_formats/public/mocks'; import type { Datatable } from '../../../../expressions/public'; @@ -15,7 +16,6 @@ import { shallowWithIntl } from '@kbn/test-jest-helpers'; import { GaugeRenderProps, GaugeArguments, - ColorStop, GaugeLabelMajorModes, GaugeTicksPositions, GaugeColorModes, diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx index efaea7dd24954..e4687d19aa63c 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx +++ b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx @@ -8,8 +8,9 @@ import React, { FC, memo, useCallback } from 'react'; import { Chart, Goal, Settings } from '@elastic/charts'; import { FormattedMessage } from '@kbn/i18n-react'; +import type { PaletteOutput } from '@kbn/coloring'; import { FieldFormat } from '../../../../field_formats/common'; -import type { CustomPaletteState, PaletteOutput } from '../../../../charts/public'; +import type { CustomPaletteState } from '../../../../charts/public'; import { EmptyPlaceholder } from '../../../../charts/public'; import { isVisDimension } from '../../../../visualizations/common/utils'; import { diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/utils/accessors.ts b/src/plugins/chart_expressions/expression_gauge/public/components/utils/accessors.ts index 31a2ff61ceaa7..cd5379f0e9ed7 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/utils/accessors.ts +++ b/src/plugins/chart_expressions/expression_gauge/public/components/utils/accessors.ts @@ -6,9 +6,10 @@ * Side Public License, v 1. */ +import type { CustomPaletteParams } from '@kbn/coloring'; import type { DatatableColumn, DatatableRow } from 'src/plugins/expressions'; import { getAccessorByDimension } from '../../../../../visualizations/common/utils'; -import { Accessors, GaugeArguments, CustomPaletteParams } from '../../../common'; +import { Accessors, GaugeArguments } from '../../../common'; export const getValueFromAccessor = ( accessor: string, diff --git a/src/plugins/chart_expressions/expression_gauge/public/services/palette_service.ts b/src/plugins/chart_expressions/expression_gauge/public/services/palette_service.ts index cfcf2a818c5bc..d18db6340fd45 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/services/palette_service.ts +++ b/src/plugins/chart_expressions/expression_gauge/public/services/palette_service.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ +import type { PaletteRegistry } from '@kbn/coloring'; import { createGetterSetter } from '../../../../kibana_utils/public'; -import { PaletteRegistry } from '../../../../charts/public'; export const [getPaletteService, setPaletteService] = createGetterSetter('palette'); diff --git a/src/plugins/chart_expressions/expression_heatmap/common/index.ts b/src/plugins/chart_expressions/expression_heatmap/common/index.ts index 56bafc2a0d612..484dee11c300b 100755 --- a/src/plugins/chart_expressions/expression_heatmap/common/index.ts +++ b/src/plugins/chart_expressions/expression_heatmap/common/index.ts @@ -15,9 +15,6 @@ export type { BrushEvent, FormatFactory, HeatmapRenderProps, - CustomPaletteParams, - ColorStop, - RequiredPaletteParamTypes, HeatmapLegendConfigResult, HeatmapGridConfigResult, HeatmapArguments, diff --git a/src/plugins/chart_expressions/expression_heatmap/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_heatmap/common/types/expression_functions.ts index 9208c8b48a29e..d96a803df92f5 100644 --- a/src/plugins/chart_expressions/expression_heatmap/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_heatmap/common/types/expression_functions.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ import { Position } from '@elastic/charts'; +import type { PaletteOutput } from '@kbn/coloring'; import { Datatable, ExpressionFunctionDefinition, @@ -13,7 +14,7 @@ import { } from '../../../../expressions'; import { ExpressionValueVisDimension } from '../../../../visualizations/common'; -import { CustomPaletteState, PaletteOutput } from '../../../../charts/common'; +import { CustomPaletteState } from '../../../../charts/common'; import { EXPRESSION_HEATMAP_NAME, EXPRESSION_HEATMAP_LEGEND_NAME, diff --git a/src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts index fa8bc5ee2e19d..67160240d284b 100644 --- a/src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts @@ -6,7 +6,8 @@ * Side Public License, v 1. */ -import type { ChartsPluginSetup, PaletteRegistry } from '../../../../charts/public'; +import type { PaletteRegistry } from '@kbn/coloring'; +import type { ChartsPluginSetup } from '../../../../charts/public'; import type { IFieldFormat, SerializedFieldFormat } from '../../../../field_formats/common'; import type { RangeSelectContext, ValueClickContext } from '../../../../embeddable/public'; import type { PersistedState } from '../../../../visualizations/public'; @@ -34,23 +35,3 @@ export type HeatmapRenderProps = HeatmapExpressionProps & { uiState: PersistedState; interactive: boolean; }; - -export interface ColorStop { - color: string; - stop: number; -} - -export interface CustomPaletteParams { - name?: string; - reverse?: boolean; - rangeType?: 'number' | 'percent'; - continuity?: 'above' | 'below' | 'all' | 'none'; - progression?: 'fixed'; - rangeMin?: number; - rangeMax?: number; - stops?: ColorStop[]; - colorStops?: ColorStop[]; - steps?: number; -} - -export type RequiredPaletteParamTypes = Required; diff --git a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx index 2121221cc9585..40d3563756c18 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx +++ b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx @@ -35,7 +35,7 @@ import { LegendColorPickerWrapperContext, LegendColorPickerWrapper, } from '../utils/get_color_picker'; -import { DEFAULT_PALETTE_NAME, defaultPaletteParams } from '../constants'; +import { defaultPaletteParams } from '../constants'; import { HeatmapIcon } from './heatmap_icon'; import './index.scss'; @@ -96,9 +96,11 @@ function computeColorRanges( ) { const paletteColors = paletteParams?.colors || - applyPaletteParams(paletteService, { type: 'palette', name: DEFAULT_PALETTE_NAME }, minMax).map( - ({ color }) => color - ); + applyPaletteParams( + paletteService, + { type: 'palette', name: defaultPaletteParams.name }, + minMax + ).map(({ color }) => color); // Repeat the first color at the beginning to cover below and above the defined palette const colors = [paletteColors[0], ...paletteColors]; diff --git a/src/plugins/chart_expressions/expression_heatmap/public/components/helpers.test.ts b/src/plugins/chart_expressions/expression_heatmap/public/components/helpers.test.ts index 7e9ccee19aa11..5199d4286e69e 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/components/helpers.test.ts +++ b/src/plugins/chart_expressions/expression_heatmap/public/components/helpers.test.ts @@ -11,10 +11,7 @@ import type { DatatableColumn } from 'src/plugins/expressions/public'; import { applyPaletteParams, getDataMinMax, - getPaletteStops, - getStepValue, remapStopsByNewInterval, - reversePalette, getSortPredicate, } from './helpers'; @@ -152,209 +149,6 @@ describe('getDataMinMax', () => { }); }); -describe('getPaletteStops', () => { - const paletteRegistry = chartPluginMock.createPaletteRegistry(); - it('should correctly compute a predefined palette stops definition from only the name', () => { - expect( - getPaletteStops(paletteRegistry, { name: 'mock' }, { dataBounds: { min: 0, max: 100 } }) - ).toEqual([ - { color: 'blue', stop: 20 }, - { color: 'yellow', stop: 70 }, - ]); - }); - - it('should correctly compute a predefined palette stops definition from explicit prevPalette (override)', () => { - expect( - getPaletteStops( - paletteRegistry, - { name: 'default' }, - { dataBounds: { min: 0, max: 100 }, prevPalette: 'mock' } - ) - ).toEqual([ - { color: 'blue', stop: 20 }, - { color: 'yellow', stop: 70 }, - ]); - }); - - it('should infer the domain from dataBounds but start from 0', () => { - expect( - getPaletteStops( - paletteRegistry, - { name: 'default', rangeType: 'number' }, - { dataBounds: { min: 1, max: 11 }, prevPalette: 'mock' } - ) - ).toEqual([ - { color: 'blue', stop: 2 }, - { color: 'yellow', stop: 7 }, - ]); - }); - - it('should override the minStop when requested', () => { - expect( - getPaletteStops( - paletteRegistry, - { name: 'default', rangeType: 'number' }, - { dataBounds: { min: 1, max: 11 }, mapFromMinValue: true } - ) - ).toEqual([ - { color: 'red', stop: 1 }, - { color: 'black', stop: 6 }, - ]); - }); - - it('should compute a display stop palette from custom colorStops defined by the user', () => { - expect( - getPaletteStops( - paletteRegistry, - { - name: 'custom', - rangeType: 'number', - colorStops: [ - { color: 'green', stop: 0 }, - { color: 'blue', stop: 40 }, - { color: 'red', stop: 80 }, - ], - }, - { dataBounds: { min: 0, max: 100 } } - ) - ).toEqual([ - { color: 'green', stop: 40 }, - { color: 'blue', stop: 80 }, - { color: 'red', stop: 100 }, - ]); - }); - - it('should compute a display stop palette from custom colorStops defined by the user - handle stop at the end', () => { - expect( - getPaletteStops( - paletteRegistry, - { - name: 'custom', - rangeType: 'number', - colorStops: [ - { color: 'green', stop: 0 }, - { color: 'blue', stop: 40 }, - { color: 'red', stop: 100 }, - ], - }, - { dataBounds: { min: 0, max: 100 } } - ) - ).toEqual([ - { color: 'green', stop: 40 }, - { color: 'blue', stop: 100 }, - { color: 'red', stop: 101 }, - ]); - }); - - it('should compute a display stop palette from custom colorStops defined by the user - handle stop at the end (fractional)', () => { - expect( - getPaletteStops( - paletteRegistry, - { - name: 'custom', - rangeType: 'number', - colorStops: [ - { color: 'green', stop: 0 }, - { color: 'blue', stop: 0.4 }, - { color: 'red', stop: 1 }, - ], - }, - { dataBounds: { min: 0, max: 1 } } - ) - ).toEqual([ - { color: 'green', stop: 0.4 }, - { color: 'blue', stop: 1 }, - { color: 'red', stop: 2 }, - ]); - }); - - it('should compute a display stop palette from custom colorStops defined by the user - stretch the stops to 100% percent', () => { - expect( - getPaletteStops( - paletteRegistry, - { - name: 'custom', - colorStops: [ - { color: 'green', stop: 0 }, - { color: 'blue', stop: 0.4 }, - { color: 'red', stop: 1 }, - ], - }, - { dataBounds: { min: 0, max: 1 } } - ) - ).toEqual([ - { color: 'green', stop: 0.4 }, - { color: 'blue', stop: 1 }, - { color: 'red', stop: 100 }, // default rangeType is percent, hence stretch to 100% - ]); - }); -}); - -describe('reversePalette', () => { - it('should correctly reverse color and stops', () => { - expect( - reversePalette([ - { color: 'red', stop: 0 }, - { color: 'green', stop: 0.5 }, - { color: 'blue', stop: 0.9 }, - ]) - ).toEqual([ - { color: 'blue', stop: 0 }, - { color: 'green', stop: 0.5 }, - { color: 'red', stop: 0.9 }, - ]); - }); -}); - -describe('getStepValue', () => { - it('should compute the next step based on the last 2 stops', () => { - expect( - getStepValue( - // first arg is taken as max reference - [ - { color: 'red', stop: 0 }, - { color: 'red', stop: 50 }, - ], - [ - { color: 'red', stop: 0 }, - { color: 'red', stop: 50 }, - ], - 100 - ) - ).toBe(50); - - expect( - getStepValue( - // first arg is taken as max reference - [ - { color: 'red', stop: 0 }, - { color: 'red', stop: 80 }, - ], - [ - { color: 'red', stop: 0 }, - { color: 'red', stop: 50 }, - ], - 90 - ) - ).toBe(10); // 90 - 80 - - expect( - getStepValue( - // first arg is taken as max reference - [ - { color: 'red', stop: 0 }, - { color: 'red', stop: 100 }, - ], - [ - { color: 'red', stop: 0 }, - { color: 'red', stop: 50 }, - ], - 100 - ) - ).toBe(1); - }); -}); - describe('getSortPredicate', () => { it('should return dataIndex if otherbucker it enabled', () => { const column = { diff --git a/src/plugins/chart_expressions/expression_heatmap/public/components/helpers.ts b/src/plugins/chart_expressions/expression_heatmap/public/components/helpers.ts index c9bfa68da9f22..b82b52e1a1a49 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/components/helpers.ts +++ b/src/plugins/chart_expressions/expression_heatmap/public/components/helpers.ts @@ -5,61 +5,22 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; -import type { Datatable, DatatableColumn } from 'src/plugins/expressions/public'; -import type { CustomPaletteParams, ColorStop } from '../../common'; + import { + PaletteOutput, + PaletteRegistry, + CustomPaletteParams, + getPaletteStops, + reversePalette, + ColorStop, CUSTOM_PALETTE, - defaultPaletteParams, DEFAULT_MAX_STOP, DEFAULT_MIN_STOP, -} from '../constants'; +} from '@kbn/coloring'; -// very simple heuristic: pick last two stops and compute a new stop based on the same distance -// if the new stop is above max, then reduce the step to reach max, or if zero then just 1. -// -// it accepts two series of stops as the function is used also when computing stops from colorStops -export function getStepValue(colorStops: ColorStop[], newColorStops: ColorStop[], max: number) { - const length = newColorStops.length; - // workout the steps from the last 2 items - const dataStep = newColorStops[length - 1].stop - newColorStops[length - 2].stop || 1; - let step = Number(dataStep.toFixed(2)); - if (max < colorStops[length - 1].stop + step) { - const diffToMax = max - colorStops[length - 1].stop; - // if the computed step goes way out of bound, fallback to 1, otherwise reach max - step = diffToMax > 0 ? diffToMax : 1; - } - return step; -} - -// Need to shift the Custom palette in order to correctly visualize it when in display mode -function shiftPalette(stops: ColorStop[], max: number) { - // shift everything right and add an additional stop at the end - const result = stops.map((entry, i, array) => ({ - ...entry, - stop: i + 1 < array.length ? array[i + 1].stop : max, - })); - if (stops[stops.length - 1].stop === max) { - // extends the range by a fair amount to make it work the extra case for the last stop === max - const computedStep = getStepValue(stops, result, max) || 1; - // do not go beyond the unit step in this case - const step = Math.min(1, computedStep); - result[stops.length - 1].stop = max + step; - } - return result; -} +import type { Datatable, DatatableColumn } from 'src/plugins/expressions/public'; -function getOverallMinMax( - params: CustomPaletteParams | undefined, - dataBounds: { min: number; max: number } -) { - const { min: dataMin, max: dataMax } = getDataMinMax(params?.rangeType, dataBounds); - const minStopValue = params?.colorStops?.[0]?.stop ?? Infinity; - const maxStopValue = params?.colorStops?.[params.colorStops.length - 1]?.stop ?? -Infinity; - const overallMin = Math.min(dataMin, minStopValue); - const overallMax = Math.max(dataMax, maxStopValue); - return { min: overallMin, max: overallMax }; -} +import { defaultPaletteParams } from '../constants'; export function getDataMinMax( rangeType: CustomPaletteParams['rangeType'] | undefined, @@ -86,63 +47,6 @@ export function remapStopsByNewInterval( }; }); } -/** - * This is a generic function to compute stops from the current parameters. - */ -export function getPaletteStops( - palettes: PaletteRegistry, - activePaletteParams: CustomPaletteParams, - // used to customize color resolution - { - prevPalette, - dataBounds, - mapFromMinValue, - defaultPaletteName, - }: { - prevPalette?: string; - dataBounds: { min: number; max: number }; - mapFromMinValue?: boolean; - defaultPaletteName?: string; - } -) { - const { min: minValue, max: maxValue } = getOverallMinMax(activePaletteParams, dataBounds); - const interval = maxValue - minValue; - const { stops: currentStops, ...otherParams } = activePaletteParams || {}; - - if (activePaletteParams.name === 'custom' && activePaletteParams?.colorStops) { - // need to generate the palette from the existing controlStops - return shiftPalette(activePaletteParams.colorStops, maxValue); - } - // generate a palette from predefined ones and customize the domain - const colorStopsFromPredefined = palettes - .get( - prevPalette || activePaletteParams?.name || defaultPaletteName || defaultPaletteParams.name - ) - .getCategoricalColors(defaultPaletteParams.steps, otherParams); - - const newStopsMin = mapFromMinValue ? minValue : interval / defaultPaletteParams.steps; - - const stops = remapStopsByNewInterval( - colorStopsFromPredefined.map((color, index) => ({ color, stop: index })), - { - newInterval: interval, - oldInterval: colorStopsFromPredefined.length, - newMin: newStopsMin, - oldMin: 0, - } - ); - return stops; -} - -export function reversePalette(paletteColorRepresentation: ColorStop[] = []) { - const stops = paletteColorRepresentation.map(({ stop }) => stop); - return paletteColorRepresentation - .map(({ color }, i) => ({ - color, - stop: stops[paletteColorRepresentation.length - i - 1], - })) - .reverse(); -} /** * Some name conventions here: @@ -169,7 +73,7 @@ export function applyPaletteParams> // make a copy of it as they have to be manipulated later on let displayStops = getPaletteStops(palettes, activePalette?.params || {}, { dataBounds, - defaultPaletteName: activePalette?.name, + defaultPaletteName: activePalette?.name ?? defaultPaletteParams.name, }); if (activePalette?.params?.reverse && activePalette?.params?.name !== CUSTOM_PALETTE) { @@ -220,6 +124,7 @@ export const findMinMaxByColumnId = ( } return minMax; }; + interface SourceParams { order?: string; orderBy?: string; diff --git a/src/plugins/chart_expressions/expression_heatmap/public/constants.ts b/src/plugins/chart_expressions/expression_heatmap/public/constants.ts index 79e24d05e3fde..a643f15c2274c 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/constants.ts +++ b/src/plugins/chart_expressions/expression_heatmap/public/constants.ts @@ -5,16 +5,17 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import type { RequiredPaletteParamTypes } from '../common'; -export const DEFAULT_PALETTE_NAME = 'temperature'; -export const FIXED_PROGRESSION = 'fixed' as const; -export const CUSTOM_PALETTE = 'custom'; -export const DEFAULT_CONTINUITY = 'above'; -export const DEFAULT_MIN_STOP = 0; -export const DEFAULT_MAX_STOP = 100; -export const DEFAULT_COLOR_STEPS = 5; +import { + RequiredPaletteParamTypes, + FIXED_PROGRESSION, + DEFAULT_CONTINUITY, + DEFAULT_MIN_STOP, + DEFAULT_MAX_STOP, + DEFAULT_COLOR_STEPS, +} from '@kbn/coloring'; + export const defaultPaletteParams: RequiredPaletteParamTypes = { - name: DEFAULT_PALETTE_NAME, + name: 'temperature', reverse: false, rangeType: 'percent', rangeMin: DEFAULT_MIN_STOP, diff --git a/src/plugins/chart_expressions/expression_heatmap/public/services/palette_service.ts b/src/plugins/chart_expressions/expression_heatmap/public/services/palette_service.ts index 4e76e3149c7a6..7185422c79cad 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/services/palette_service.ts +++ b/src/plugins/chart_expressions/expression_heatmap/public/services/palette_service.ts @@ -6,8 +6,9 @@ * Side Public License, v 1. */ +import type { PaletteRegistry } from '@kbn/coloring'; import { createGetterSetter } from '../../../../kibana_utils/public'; -import { PaletteRegistry, ChartsPluginSetup } from '../../../../charts/public'; +import { ChartsPluginSetup } from '../../../../charts/public'; export const [getPaletteService, setPaletteService] = createGetterSetter('palette'); diff --git a/src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts index 894be5c4e6516..deb5f5e027abf 100644 --- a/src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import type { PaletteOutput } from '@kbn/coloring'; import { Datatable, ExpressionFunctionDefinition, @@ -13,7 +14,7 @@ import { Style, } from '../../../../expressions'; import { ExpressionValueVisDimension } from '../../../../visualizations/common'; -import { ColorMode, CustomPaletteState, PaletteOutput } from '../../../../charts/common'; +import { ColorMode, CustomPaletteState } from '../../../../charts/common'; import { VisParams, visType, LabelPositionType } from './expression_renderers'; import { EXPRESSION_METRIC_NAME } from '../constants'; diff --git a/src/plugins/chart_expressions/expression_metric/public/services/palette_service.ts b/src/plugins/chart_expressions/expression_metric/public/services/palette_service.ts index cfcf2a818c5bc..d18db6340fd45 100644 --- a/src/plugins/chart_expressions/expression_metric/public/services/palette_service.ts +++ b/src/plugins/chart_expressions/expression_metric/public/services/palette_service.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ +import type { PaletteRegistry } from '@kbn/coloring'; import { createGetterSetter } from '../../../../kibana_utils/public'; -import { PaletteRegistry } from '../../../../charts/public'; export const [getPaletteService, setPaletteService] = createGetterSetter('palette'); diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts index c6f29ef90c8e9..7ac7e9deedc20 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts @@ -7,10 +7,10 @@ */ import { Position } from '@elastic/charts'; +import type { PaletteOutput } from '@kbn/coloring'; import { Datatable, DatatableColumn } from '../../../../expressions/common'; import { SerializedFieldFormat } from '../../../../field_formats/common'; import { ExpressionValueVisDimension } from '../../../../visualizations/common'; -import { PaletteOutput } from '../../../../charts/common'; import { ChartTypes, ExpressionValuePartitionLabels } from './expression_functions'; export enum EmptySizeRatios { diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/__mocks__/palettes.ts b/src/plugins/chart_expressions/expression_partition_vis/public/__mocks__/palettes.ts index 5637acfdbee10..1c7d60923118c 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/__mocks__/palettes.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/__mocks__/palettes.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { PaletteDefinition, SeriesLayer } from '../../../../charts/public'; +import type { PaletteDefinition, SeriesLayer } from '@kbn/coloring'; export const getPaletteRegistry = () => { const colors = [ diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx index c843bf189d77f..e8ab85f2877ff 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx @@ -20,7 +20,8 @@ import { SeriesIdentifier, } from '@elastic/charts'; import { useEuiTheme } from '@elastic/eui'; -import { LegendToggle, ChartsPluginSetup, PaletteRegistry } from '../../../../charts/public'; +import type { PaletteRegistry } from '@kbn/coloring'; +import { LegendToggle, ChartsPluginSetup } from '../../../../charts/public'; import type { PersistedState } from '../../../../visualizations/public'; import { getColumnByAccessor } from '../../../../visualizations/common/utils'; import { diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color.test.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color.test.ts index efeb1f038232d..d951d962d67f2 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { PaletteDefinition, PaletteOutput } from '../../../../../charts/public'; +import type { PaletteOutput, PaletteDefinition } from '@kbn/coloring'; import { chartPluginMock } from '../../../../../charts/public/mocks'; import { Datatable } from '../../../../../expressions'; import { byDataColorPaletteMap } from './get_color'; diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color.ts index 183bf91e59740..f6d88d1fe802e 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color.ts @@ -7,14 +7,9 @@ */ import { ShapeTreeNode } from '@elastic/charts'; import { isEqual } from 'lodash'; +import type { PaletteRegistry, SeriesLayer, PaletteOutput, PaletteDefinition } from '@kbn/coloring'; import type { FieldFormatsStart } from '../../../../../field_formats/public'; -import { - SeriesLayer, - PaletteRegistry, - lightenColor, - PaletteDefinition, - PaletteOutput, -} from '../../../../../charts/public'; +import { lightenColor } from '../../../../../charts/public'; import type { Datatable, DatatableRow } from '../../../../../expressions/public'; import { BucketColumns, ChartTypes, PartitionVisParams } from '../../../common/types'; import { DistinctSeries, getDistinctSeries } from '../get_distinct_series'; diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.test.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.test.ts index c07bbf3ec6e16..0cc0fea93e6fe 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ import { ShapeTreeNode } from '@elastic/charts'; -import { PaletteDefinition, SeriesLayer } from '../../../../../charts/public'; +import type { PaletteDefinition, SeriesLayer } from '@kbn/coloring'; import { dataPluginMock } from '../../../../../data/public/mocks'; import type { DataPublicPluginStart } from '../../../../../data/public'; import { getColor } from './get_color'; diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts index 9f27ff628cf97..8280e7bd2a896 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts @@ -7,9 +7,9 @@ */ import { Datum, PartitionLayer } from '@elastic/charts'; +import type { PaletteRegistry } from '@kbn/coloring'; import { FieldFormat } from '../../../../../field_formats/common'; import type { FieldFormatsStart } from '../../../../../field_formats/public'; -import { PaletteRegistry } from '../../../../../charts/public'; import type { Datatable, DatatableRow } from '../../../../../expressions/public'; import { BucketColumns, ChartTypes, PartitionVisParams } from '../../../common/types'; import { sortPredicateByType } from './sort_predicate'; diff --git a/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts index 7463b6735bfe3..01c29ccc0e77d 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts @@ -7,7 +7,7 @@ */ import { $Values } from '@kbn/utility-types'; -import { PaletteOutput } from '../../../../charts/common'; +import type { PaletteOutput } from '@kbn/coloring'; import { Datatable, ExpressionFunctionDefinition, diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/__mocks__/palettes.ts b/src/plugins/chart_expressions/expression_tagcloud/public/__mocks__/palettes.ts index 7ca00b58f5624..0382a98bb76ec 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/public/__mocks__/palettes.ts +++ b/src/plugins/chart_expressions/expression_tagcloud/public/__mocks__/palettes.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { PaletteDefinition, SeriesLayer } from '../../../../charts/public'; +import type { PaletteDefinition, SeriesLayer } from '@kbn/coloring'; import { random } from 'lodash'; export const getPaletteRegistry = () => { diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx index 4c65c5837868d..5860c209f8be8 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx +++ b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx @@ -11,7 +11,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { throttle } from 'lodash'; import { EuiIconTip, EuiResizeObserver } from '@elastic/eui'; import { Chart, Settings, Wordcloud, RenderChangeListener } from '@elastic/charts'; -import type { PaletteRegistry, PaletteOutput } from '../../../../charts/public'; +import type { PaletteRegistry, PaletteOutput } from '@kbn/coloring'; import { IInterpreterRenderHandlers } from '../../../../expressions/public'; import { getFormatService } from '../format_service'; import { diff --git a/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts index 4bafffc065835..f09ad04e77ddb 100644 --- a/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts @@ -7,7 +7,7 @@ */ import { Position } from '@elastic/charts'; -import { PaletteOutput } from 'src/plugins/charts/common'; +import type { PaletteOutput } from '@kbn/coloring'; import { Datatable, DatatableRow } from 'src/plugins/expressions'; import { LayerTypes } from '../constants'; import { DataLayerConfigResult, LensMultiTable, XYArgs } from '../types'; diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index 98889da771c04..044e75f95fc57 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -8,8 +8,8 @@ import { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/charts'; import { $Values } from '@kbn/utility-types'; +import type { PaletteOutput } from '@kbn/coloring'; import { Datatable } from '../../../../expressions'; -import { PaletteOutput } from '../../../../charts/common'; import { EventAnnotationOutput } from '../../../../event_annotation/common'; import { AxisExtentModes, diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index d69e8e8b645fa..eace75970c67a 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -38,6 +38,7 @@ import { } from '@elastic/charts'; import { IconType } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { PaletteRegistry, SeriesLayer } from '@kbn/coloring'; import type { Datatable, DatatableRow, DatatableColumn } from '../../../../expressions/public'; import { RenderMode } from '../../../../expressions/common'; import { FieldFormat } from '../../../../field_formats/common'; @@ -49,8 +50,6 @@ import { EventAnnotationServiceType } from '../../../../event_annotation/public' import { ChartsPluginSetup, ChartsPluginStart, - PaletteRegistry, - SeriesLayer, useActiveCursor, } from '../../../../../plugins/charts/public'; import { MULTILAYER_TIME_AXIS_STYLE } from '../../../../../plugins/charts/common'; diff --git a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx index 5e4921e85af62..170f4fb9bb87b 100644 --- a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx @@ -11,7 +11,8 @@ import { I18nProvider } from '@kbn/i18n-react'; import { ThemeServiceStart } from 'kibana/public'; import React from 'react'; import ReactDOM from 'react-dom'; -import type { ChartsPluginStart, PaletteRegistry } from '../../../../charts/public'; +import type { PaletteRegistry } from '@kbn/coloring'; +import type { ChartsPluginStart } from '../../../../charts/public'; import { EventAnnotationServiceType } from '../../../../event_annotation/public'; import { ExpressionRenderDefinition } from '../../../../expressions'; import { FormatFactory } from '../../../../field_formats/common'; From 0ed760d07855cac57a01b81d70aa4105a62b755e Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Fri, 8 Apr 2022 17:47:42 +0500 Subject: [PATCH 16/42] [Step 2] Update @kbn/coloring imports for Classical Visualizations (#129725) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/components/controls/palette_picker.tsx | 2 +- src/plugins/vis_types/pie/public/editor/components/pie.tsx | 3 +-- src/plugins/vis_types/pie/public/to_ast.ts | 2 +- .../vis_types/tagcloud/public/components/tag_cloud_options.tsx | 2 +- src/plugins/vis_types/tagcloud/public/to_ast.ts | 2 +- src/plugins/vis_types/tagcloud/public/types.ts | 3 ++- .../public/application/components/palette_picker.tsx | 2 +- .../public/application/components/timeseries_visualization.tsx | 3 +-- .../public/application/components/vis_types/index.ts | 3 +-- .../public/application/lib/get_split_by_terms_color.ts | 3 ++- .../vis_types/timeseries/public/trigger_action/index.ts | 2 +- .../components/options/point_series/elastic_charts_options.tsx | 3 +-- src/plugins/vis_types/xy/public/types/param.ts | 3 ++- src/plugins/vis_types/xy/public/vis_component.tsx | 3 +-- src/plugins/visualizations/public/vis_types/types.ts | 2 +- 15 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/plugins/vis_default_editor/public/components/controls/palette_picker.tsx b/src/plugins/vis_default_editor/public/components/controls/palette_picker.tsx index ee6ee405c479d..29d8924a25077 100644 --- a/src/plugins/vis_default_editor/public/components/controls/palette_picker.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/palette_picker.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; +import type { PaletteOutput, PaletteRegistry } from '@kbn/coloring'; import { EuiColorPalettePicker, EuiColorPalettePickerPaletteProps } from '@elastic/eui'; import { EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; diff --git a/src/plugins/vis_types/pie/public/editor/components/pie.tsx b/src/plugins/vis_types/pie/public/editor/components/pie.tsx index a8324570a9259..9be0726ae9eb3 100644 --- a/src/plugins/vis_types/pie/public/editor/components/pie.tsx +++ b/src/plugins/vis_types/pie/public/editor/components/pie.tsx @@ -22,7 +22,7 @@ import { import { Position } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; - +import type { PaletteRegistry } from '@kbn/coloring'; import { BasicOptions, SwitchOption, @@ -33,7 +33,6 @@ import { } from '../../../../../vis_default_editor/public'; import { VisEditorOptionsProps } from '../../../../../visualizations/public'; import { TruncateLabelsOption } from './truncate_labels'; -import { PaletteRegistry } from '../../../../../charts/public'; import { DEFAULT_PERCENT_DECIMALS } from '../../../common'; import { PieTypeProps } from '../../types'; import { diff --git a/src/plugins/vis_types/pie/public/to_ast.ts b/src/plugins/vis_types/pie/public/to_ast.ts index 2dcc1d916a86e..37f01fcc59340 100644 --- a/src/plugins/vis_types/pie/public/to_ast.ts +++ b/src/plugins/vis_types/pie/public/to_ast.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ +import type { PaletteOutput } from '@kbn/coloring'; import { getVisSchemas, VisToExpressionAst, SchemaConfig } from '../../../visualizations/public'; import { buildExpression, buildExpressionFunction } from '../../../expressions/public'; -import { PaletteOutput } from '../../../charts/common'; import { PIE_VIS_EXPRESSION_NAME, PARTITION_LABELS_FUNCTION, diff --git a/src/plugins/vis_types/tagcloud/public/components/tag_cloud_options.tsx b/src/plugins/vis_types/tagcloud/public/components/tag_cloud_options.tsx index ff5f1e6edd6f8..870a05bcb9277 100644 --- a/src/plugins/vis_types/tagcloud/public/components/tag_cloud_options.tsx +++ b/src/plugins/vis_types/tagcloud/public/components/tag_cloud_options.tsx @@ -9,7 +9,7 @@ import React, { useState, useEffect } from 'react'; import { EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import type { PaletteRegistry } from '../../../../charts/public'; +import type { PaletteRegistry } from '@kbn/coloring'; import { VisEditorOptionsProps } from '../../../../visualizations/public'; import { SelectOption, SwitchOption, PalettePicker } from '../../../../vis_default_editor/public'; import { ValidatedDualRange } from '../../../../kibana_react/public'; diff --git a/src/plugins/vis_types/tagcloud/public/to_ast.ts b/src/plugins/vis_types/tagcloud/public/to_ast.ts index e9ef8f84ba55b..c66660e78a78a 100644 --- a/src/plugins/vis_types/tagcloud/public/to_ast.ts +++ b/src/plugins/vis_types/tagcloud/public/to_ast.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { PaletteOutput } from 'src/plugins/charts/common'; +import type { PaletteOutput } from '@kbn/coloring'; import { EsaggsExpressionFunctionDefinition, IndexPatternLoadExpressionFunctionDefinition, diff --git a/src/plugins/vis_types/tagcloud/public/types.ts b/src/plugins/vis_types/tagcloud/public/types.ts index 996555ae99f83..e3c5c057a3405 100644 --- a/src/plugins/vis_types/tagcloud/public/types.ts +++ b/src/plugins/vis_types/tagcloud/public/types.ts @@ -5,7 +5,8 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import type { ChartsPluginSetup, PaletteOutput } from '../../../charts/public'; +import type { PaletteOutput } from '@kbn/coloring'; +import type { ChartsPluginSetup } from '../../../charts/public'; import { ExpressionValueVisDimension } from '../../../visualizations/public'; interface TagCloudCommonParams { diff --git a/src/plugins/vis_types/timeseries/public/application/components/palette_picker.tsx b/src/plugins/vis_types/timeseries/public/application/components/palette_picker.tsx index 749d6ca62bfa9..56d763ad64eb5 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/palette_picker.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/palette_picker.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; +import type { PaletteRegistry, PaletteOutput } from '@kbn/coloring'; import { EuiColorPalettePicker } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { rainbowColors } from '../lib/rainbow_colors'; diff --git a/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx b/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx index 5f33fc34e7d67..f2bf6425cf775 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx @@ -14,8 +14,7 @@ import { XYChartSeriesIdentifier, GeometryValue } from '@elastic/charts'; import { IUiSettingsClient } from 'src/core/public'; import { IInterpreterRenderHandlers } from 'src/plugins/expressions'; import { PersistedState } from 'src/plugins/visualizations/public'; -import { PaletteRegistry } from 'src/plugins/charts/public'; - +import type { PaletteRegistry } from '@kbn/coloring'; import { TimeseriesLoading } from './timeseries_loading'; import { TimeseriesVisTypes } from './vis_types'; import type { FetchedIndexPattern, PanelData, TimeseriesVisData } from '../../../common/types'; diff --git a/src/plugins/vis_types/timeseries/public/application/components/vis_types/index.ts b/src/plugins/vis_types/timeseries/public/application/components/vis_types/index.ts index 512a9d1ff9169..9061e71283e0c 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/vis_types/index.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/index.ts @@ -10,8 +10,7 @@ import React, { lazy } from 'react'; import { XYChartSeriesIdentifier, GeometryValue } from '@elastic/charts'; import { IUiSettingsClient } from 'src/core/public'; import { PersistedState } from 'src/plugins/visualizations/public'; -import { PaletteRegistry } from 'src/plugins/charts/public'; - +import type { PaletteRegistry } from '@kbn/coloring'; import { TimeseriesVisParams } from '../../../types'; import type { TimeseriesVisData, PanelData } from '../../../../common/types'; import type { FieldFormatMap } from '../../../../../../data/common'; diff --git a/src/plugins/vis_types/timeseries/public/application/lib/get_split_by_terms_color.ts b/src/plugins/vis_types/timeseries/public/application/lib/get_split_by_terms_color.ts index e02965c1b0657..ad6fd361400b4 100644 --- a/src/plugins/vis_types/timeseries/public/application/lib/get_split_by_terms_color.ts +++ b/src/plugins/vis_types/timeseries/public/application/lib/get_split_by_terms_color.ts @@ -5,7 +5,8 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; + +import type { PaletteRegistry, PaletteOutput } from '@kbn/coloring'; import { PALETTES } from '../../../common/enums'; import type { PanelData } from '../../../common/types'; import { computeGradientFinalColor } from './compute_gradient_final_color'; diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/index.ts b/src/plugins/vis_types/timeseries/public/trigger_action/index.ts index 15fb1aec2974c..5cdbc72d08fe3 100644 --- a/src/plugins/vis_types/timeseries/public/trigger_action/index.ts +++ b/src/plugins/vis_types/timeseries/public/trigger_action/index.ts @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { PaletteOutput } from '../../../../charts/public'; +import type { PaletteOutput } from '@kbn/coloring'; import type { NavigateToLensContext, VisualizeEditorLayersContext, diff --git a/src/plugins/vis_types/xy/public/editor/components/options/point_series/elastic_charts_options.tsx b/src/plugins/vis_types/xy/public/editor/components/options/point_series/elastic_charts_options.tsx index 1c93fe92b79af..b073f32ea3a58 100644 --- a/src/plugins/vis_types/xy/public/editor/components/options/point_series/elastic_charts_options.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/point_series/elastic_charts_options.tsx @@ -7,7 +7,7 @@ */ import React, { useState, useEffect } from 'react'; - +import type { PaletteRegistry } from '@kbn/coloring'; import { i18n } from '@kbn/i18n'; import { METRIC_TYPE } from '@kbn/analytics'; import { EuiFormRow, EuiRange } from '@elastic/eui'; @@ -16,7 +16,6 @@ import { SwitchOption, PalettePicker, } from '../../../../../../../vis_default_editor/public'; -import { PaletteRegistry } from '../../../../../../../charts/public'; import { ChartType } from '../../../../../common'; import { VisParams } from '../../../../types'; diff --git a/src/plugins/vis_types/xy/public/types/param.ts b/src/plugins/vis_types/xy/public/types/param.ts index 687e291976606..c0913226f9614 100644 --- a/src/plugins/vis_types/xy/public/types/param.ts +++ b/src/plugins/vis_types/xy/public/types/param.ts @@ -7,7 +7,8 @@ */ import type { Fit, Position } from '@elastic/charts'; -import type { Style, Labels, PaletteOutput } from '../../../../charts/public'; +import type { PaletteOutput } from '@kbn/coloring'; +import type { Style, Labels } from '../../../../charts/public'; import type { SchemaConfig, ExpressionValueXYDimension, diff --git a/src/plugins/vis_types/xy/public/vis_component.tsx b/src/plugins/vis_types/xy/public/vis_component.tsx index a77037ce025cb..05d2d0b0765cd 100644 --- a/src/plugins/vis_types/xy/public/vis_component.tsx +++ b/src/plugins/vis_types/xy/public/vis_component.tsx @@ -7,7 +7,7 @@ */ import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; - +import type { PaletteRegistry } from '@kbn/coloring'; import { Chart, ElementClickListener, @@ -29,7 +29,6 @@ import { LegendToggle, getBrushFromChartBrushEventFn, ClickTriggerEvent, - PaletteRegistry, useActiveCursor, } from '../../../charts/public'; import { Datatable, IInterpreterRenderHandlers } from '../../../expressions/public'; diff --git a/src/plugins/visualizations/public/vis_types/types.ts b/src/plugins/visualizations/public/vis_types/types.ts index 14da9eb887e0c..ee4459e4e772e 100644 --- a/src/plugins/visualizations/public/vis_types/types.ts +++ b/src/plugins/visualizations/public/vis_types/types.ts @@ -8,10 +8,10 @@ import type { IconType } from '@elastic/eui'; import type { ReactNode } from 'react'; +import type { PaletteOutput } from '@kbn/coloring'; import type { Adapters } from 'src/plugins/inspector'; import type { AggGroupNames, AggParam, AggGroupName, Query } from '../../../data/public'; import type { DataView } from '../../../data_views/public'; -import { PaletteOutput } from '../../../charts/public'; import type { Vis, VisEditorOptionsProps, VisParams, VisToExpressionAst } from '../types'; import { VisGroups } from './vis_groups_enum'; From 5dbd17760cbed3e431db062faea52b0f677893c2 Mon Sep 17 00:00:00 2001 From: Karl Godard Date: Fri, 8 Apr 2022 11:59:05 -0700 Subject: [PATCH 17/42] [Session View] Fixed issue where details panel was showing wrong data for leader processes (#129816) * added missing ECS fields for real, saved, supplemental groups / user. fixed a bug in details panel which was showing incorrect information for the leader processes * fix function signature mismatch Co-authored-by: mitodrummer --- .../constants/session_view_process.mock.ts | 5 ++ .../common/types/process_tree/index.ts | 6 ++ .../public/components/process_tree/hooks.ts | 7 +- .../session_view_detail_panel/helpers.ts | 65 +++++++++---------- 4 files changed, 45 insertions(+), 38 deletions(-) diff --git a/x-pack/plugins/session_view/common/mocks/constants/session_view_process.mock.ts b/x-pack/plugins/session_view/common/mocks/constants/session_view_process.mock.ts index df16f26730d98..581134eae1782 100644 --- a/x-pack/plugins/session_view/common/mocks/constants/session_view_process.mock.ts +++ b/x-pack/plugins/session_view/common/mocks/constants/session_view_process.mock.ts @@ -1325,6 +1325,7 @@ export const childProcessMock: Process = { } as ProcessEvent), isUserEntered: () => false, getMaxAlertLevel: () => null, + getEndTime: () => '', }; export const processMock: Process = { @@ -1497,6 +1498,7 @@ export const processMock: Process = { } as ProcessEvent), isUserEntered: () => false, getMaxAlertLevel: () => null, + getEndTime: () => '', }; export const sessionViewBasicProcessMock: Process = { @@ -1504,6 +1506,7 @@ export const sessionViewBasicProcessMock: Process = { events: mockEvents, hasExec: () => true, isUserEntered: () => true, + getEndTime: () => '', }; export const sessionViewAlertProcessMock: Process = { @@ -1514,6 +1517,7 @@ export const sessionViewAlertProcessMock: Process = { getAlerts: () => mockAlerts, hasExec: () => true, isUserEntered: () => true, + getEndTime: () => '', }; export const mockProcessMap = mockEvents.reduce( @@ -1540,6 +1544,7 @@ export const mockProcessMap = mockEvents.reduce( getDetails: () => event, isUserEntered: () => false, getMaxAlertLevel: () => null, + getEndTime: () => '', }; return processMap; }, diff --git a/x-pack/plugins/session_view/common/types/process_tree/index.ts b/x-pack/plugins/session_view/common/types/process_tree/index.ts index 2020884b141a6..c4cb85a81dd0c 100644 --- a/x-pack/plugins/session_view/common/types/process_tree/index.ts +++ b/x-pack/plugins/session_view/common/types/process_tree/index.ts @@ -76,6 +76,11 @@ export interface ProcessFields { end?: string; user?: User; group?: Group; + real_user?: User; + real_group?: Group; + saved_user?: User; + saved_group?: Group; + supplemental_groups?: Group[]; exit_code?: number; entry_meta?: EntryMeta; tty?: Teletype; @@ -173,6 +178,7 @@ export interface Process { isUserEntered(): boolean; getMaxAlertLevel(): number | null; getChildren(verboseMode: boolean): Process[]; + getEndTime(): string; } export type ProcessMap = { diff --git a/x-pack/plugins/session_view/public/components/process_tree/hooks.ts b/x-pack/plugins/session_view/public/components/process_tree/hooks.ts index 9cbf1ca2b0f84..1725abdcdb5b9 100644 --- a/x-pack/plugins/session_view/public/components/process_tree/hooks.ts +++ b/x-pack/plugins/session_view/public/components/process_tree/hooks.ts @@ -148,11 +148,8 @@ export class ProcessImpl implements Process { } getEndTime() { - const endEvent = this.filterEventsByAction(this.events, EventAction.end); - if (endEvent.length === 0) { - return ''; - } - return endEvent[endEvent.length - 1]['@timestamp']; + const endEvent = this.findEventByAction(this.events, EventAction.end); + return endEvent?.['@timestamp'] || ''; } // isUserEntered is a best guess at which processes were initiated by a real person diff --git a/x-pack/plugins/session_view/public/components/session_view_detail_panel/helpers.ts b/x-pack/plugins/session_view/public/components/session_view_detail_panel/helpers.ts index a96330a35b084..083bc35ac49d4 100644 --- a/x-pack/plugins/session_view/public/components/session_view_detail_panel/helpers.ts +++ b/x-pack/plugins/session_view/public/components/session_view_detail_panel/helpers.ts @@ -6,7 +6,6 @@ */ import { EventAction, Process, ProcessFields } from '../../../common/types/process_tree'; import { DetailPanelProcess, EuiTabProps } from '../../types'; -import { ProcessImpl } from '../process_tree/hooks'; const FILTER_FORKS_EXECS = [EventAction.fork, EventAction.exec]; @@ -57,33 +56,39 @@ export const getDetailPanelProcess = (process: Process | undefined) => { }; } - const endProcesses = new ProcessImpl(process.id); + const details = process.getDetails(); processData.id = process.id; - processData.start = process.events[0]?.['@timestamp'] ?? ''; + processData.start = details.process?.start ?? ''; processData.args = []; processData.executable = []; - process.events.forEach((event) => { - if (!processData.userName) { - processData.userName = event.user?.name ?? ''; - } - if (!processData.groupName) { - processData.groupName = event.group?.name ?? ''; - } - if (!processData.pid) { - processData.pid = event.process?.pid; - } - if (!processData.working_directory) { - processData.working_directory = event.process?.working_directory ?? ''; - } - if (!processData.tty) { - processData.tty = event.process?.tty; - } + if (!processData.userName) { + processData.userName = details.process?.user?.name ?? ''; + } + if (!processData.groupName) { + processData.groupName = details.process?.group?.name ?? ''; + } + if (!processData.pid) { + processData.pid = details.process?.pid; + } + if (!processData.working_directory) { + processData.working_directory = details.process?.working_directory ?? ''; + } + if (!processData.tty) { + processData.tty = details.process?.tty; + } + if (details.process?.args && details.process.args.length > 0) { + processData.args = details.process.args; + } + if (details.process?.exit_code !== undefined) { + processData.exit_code = details.process.exit_code; + } - if (event.process?.args && event.process.args.length > 0) { - processData.args = event.process.args; - } + // we grab the executable from each process lifecycle event to give an indication + // of the processes journey. Processes can sometimes exec multiple times, so it's good + // information to have. + process.events.forEach((event) => { if ( event.process?.executable && event.event?.action && @@ -91,19 +96,13 @@ export const getDetailPanelProcess = (process: Process | undefined) => { ) { processData.executable.push([event.process.executable, `(${event.event.action})`]); } - if (event.process?.exit_code !== undefined) { - processData.exit_code = event.process.exit_code; - } - endProcesses.addEvent(event); }); - processData.end = endProcesses.getEndTime() as string; - processData.entryLeader = getDetailPanelProcessLeader(process.events[0]?.process?.entry_leader); - processData.sessionLeader = getDetailPanelProcessLeader( - process.events[0]?.process?.session_leader - ); - processData.groupLeader = getDetailPanelProcessLeader(process.events[0]?.process?.group_leader); - processData.parent = getDetailPanelProcessLeader(process.events[0]?.process?.parent); + processData.end = process.getEndTime(); + processData.entryLeader = getDetailPanelProcessLeader(details?.process?.entry_leader); + processData.sessionLeader = getDetailPanelProcessLeader(details?.process?.session_leader); + processData.groupLeader = getDetailPanelProcessLeader(details?.process?.group_leader); + processData.parent = getDetailPanelProcessLeader(details?.process?.parent); return processData; }; From eb0ef19583089b8de61747195c117d86f4ed77eb Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 8 Apr 2022 20:42:37 +0100 Subject: [PATCH 18/42] chore(NA): upgrades bazel rules nodejs to v5 (#129522) * chore(NA): upgrade bazel into v5 * chore(NA): initial work to upgrade to rules v5 * chore(NA): remove @elastic/datemath jest unit test for eslint plugin imports * chore(NA): removed packager tsc_wrapped added my mistake --- .eslintrc.js | 2 +- .github/CODEOWNERS | 2 +- WORKSPACE.bazel | 16 +++--- .../monorepo-packages.asciidoc | 2 +- package.json | 7 +-- packages/BUILD.bazel | 4 +- packages/elastic-apm-synthtrace/BUILD.bazel | 4 +- .../elastic-apm-synthtrace/src/scripts/run.ts | 2 +- .../.npmignore | 0 .../BUILD.bazel | 4 +- .../README.md | 2 +- .../jest.config.js | 2 +- .../package.json | 4 +- .../src/index.test.js | 0 .../src/index.ts | 0 .../tsconfig.json | 0 .../kbn-docs-utils/src/api_docs/utils.test.ts | 2 +- .../resolve_kibana_import.test.ts | 9 ---- packages/kbn-pm/dist/index.js | 2 +- packages/kbn-pm/src/commands/bootstrap.ts | 2 +- .../BUILD.bazel | 4 +- .../src/param_is_valid/index.ts | 2 +- .../BUILD.bazel | 4 +- .../src/parse_schedule_dates/index.ts | 2 +- packages/kbn-type-summarizer/BUILD.bazel | 5 +- packages/kbn-ui-shared-deps-npm/BUILD.bazel | 1 - packages/kbn-ui-shared-deps-src/BUILD.bazel | 4 +- .../kbn-ui-shared-deps-src/src/definitions.js | 1 + packages/kbn-ui-shared-deps-src/src/entry.js | 2 +- src/dev/bazel/pkg_npm_types.bzl | 2 +- .../public/services/kibana/options_list.ts | 2 +- .../data/common/query/timefilter/get_time.ts | 2 +- .../lib/time_buckets/calc_es_interval.ts | 2 +- .../buckets/lib/time_buckets/time_buckets.ts | 2 +- .../invalid_es_calendar_interval_error.ts | 2 +- .../least_common_interval.ts | 2 +- .../date_interval_utils/parse_es_interval.ts | 2 +- .../date_interval_utils/parse_interval.ts | 2 +- .../date_interval_utils/to_absolute_dates.ts | 2 +- .../providers/value_suggestion_provider.ts | 2 +- .../timefilter/lib/validate_timerange.ts | 2 +- .../main/components/chart/histogram.tsx | 2 +- .../components/chart/point_series.test.ts | 2 +- .../main/components/chart/point_series.ts | 2 +- .../application/main/utils/get_dimensions.ts | 2 +- .../main/utils/validate_time_range.ts | 2 +- .../filter_editor/lib/filter_editor_utils.ts | 2 +- .../query_string_input/query_bar_top_row.tsx | 2 +- .../components/controls/date_ranges.tsx | 2 +- .../timeseries/common/interval_regexp.ts | 2 +- .../default_search_capabilities.ts | 2 +- .../rollup_search_capabilities.test.ts | 2 +- .../search_strategies/lib/interval_helper.ts | 2 +- .../lib/vis_data/helpers/get_bucket_size.ts | 2 +- .../lib/vis_data/helpers/parse_interval.js | 2 +- .../vis_data/helpers/unit_to_seconds.test.ts | 2 +- .../lib/vis_data/helpers/unit_to_seconds.ts | 2 +- .../vega/public/vega_view/vega_base_view.js | 2 +- .../vislib/visualizations/time_marker.js | 2 +- .../service_group_save/save_modal.tsx | 2 +- .../url_params_context/helpers.test.ts | 2 +- .../context/url_params_context/helpers.ts | 2 +- .../alerts/register_anomaly_alert_type.ts | 2 +- .../functions/common/timefilter.ts | 2 +- .../common/lib/build_embeddable_filters.ts | 2 +- .../filter_views/time_filter.ts | 2 +- .../common/services/time_buckets.js | 2 +- .../common/utils/parse_interval.ts | 2 +- .../full_time_range_selector_service.ts | 2 +- .../components/metrics_node_details_link.tsx | 2 +- .../hooks/use_infrastructure_node_metrics.ts | 2 +- .../log_text_stream/loading_item_view.tsx | 2 +- .../page_results_content.tsx | 2 +- .../use_log_entry_rate_results_url_state.tsx | 2 +- .../inventory_view/hooks/use_waffle_time.ts | 2 +- .../components/node_details_page.tsx | 2 +- .../metric_detail/hooks/use_metrics_time.ts | 2 +- .../components/chart_context_menu.tsx | 2 +- .../hooks/use_metrics_explorer_data.ts | 2 +- .../utils/convert_interval_to_string.ts | 2 +- x-pack/plugins/infra/public/utils/datemath.ts | 2 +- .../preview_metric_anomaly_alert.ts | 2 +- .../calculate_bucket_size/interval_regex.ts | 2 +- .../infra/server/utils/round_timestamp.ts | 2 +- .../indexpattern_datasource/field_item.tsx | 2 +- .../plugins/lens/server/routes/field_stats.ts | 2 +- x-pack/plugins/ml/common/util/date_utils.ts | 2 +- .../plugins/ml/common/util/parse_interval.ts | 2 +- .../full_time_range_selector_service.ts | 2 +- .../application/util/chart_utils.test.js | 2 +- .../public/application/util/time_buckets.js | 2 +- .../components/date_range_picker.tsx | 2 +- .../observability/public/utils/date.ts | 2 +- .../components/super_date_picker/index.tsx | 2 +- .../url_state/normalize_time_range.test.ts | 2 +- .../public/common/hooks/eql/helpers.ts | 2 +- .../public/common/hooks/eql/types.ts | 2 +- .../common/hooks/eql/use_eql_preview.test.ts | 2 +- .../common/utils/default_date_settings.ts | 2 +- .../components/alerts_table/actions.tsx | 2 +- .../components/rules/rule_preview/helpers.ts | 2 +- .../components/rules/rule_preview/index.tsx | 2 +- .../rules/rule_preview/preview_histogram.tsx | 2 +- .../rules/rule_preview/use_preview_route.tsx | 2 +- .../containers/detection_engine/rules/api.ts | 2 +- .../rules/use_preview_rule.ts | 2 +- .../detection_engine/rules/create/helpers.ts | 2 +- .../pages/detection_engine/rules/helpers.tsx | 2 +- .../management/pages/endpoint_hosts/utils.ts | 2 +- .../activity_log_date_range_picker/index.tsx | 2 +- .../transforms/utils/adjust_timerange.ts | 2 +- .../signals/executors/eql.test.ts | 2 +- .../signals/executors/ml.test.ts | 2 +- .../signals/executors/threshold.test.ts | 2 +- .../signals/find_ml_signals.ts | 2 +- .../lib/detection_engine/signals/utils.ts | 2 +- .../common/parse_interval.ts | 2 +- .../components/rule_error_log.tsx | 2 +- .../components/rule_event_log_list.tsx | 2 +- .../drilldowns/url_drilldown/handlebars.ts | 2 +- .../common/lib/get_histogram_interval.ts | 2 +- .../common/charts/duration_charts.test.tsx | 2 +- .../common/charts/ping_histogram.test.tsx | 2 +- .../monitor/monitor_charts.test.tsx | 2 +- .../public/hooks/use_url_params.test.tsx | 2 +- .../get_supported_url_params.test.ts | 2 +- .../url_params/parse_absolute_date.test.ts | 2 +- .../helper/url_params/parse_absolute_date.ts | 2 +- .../uptime/server/lib/alerts/status_check.ts | 2 +- .../url_params_context/helpers.test.ts | 2 +- .../context/url_params_context/helpers.ts | 2 +- .../watch_visualization.tsx | 2 +- .../watcher/public/legacy/calc_es_interval.ts | 2 +- .../invalid_es_calendar_interval_error.ts | 2 +- .../parse_es_interval/parse_es_interval.ts | 2 +- .../tests/get_rule_execution_events.ts | 2 +- .../apps/rollup_job/hybrid_index_pattern.js | 2 +- .../functional/apps/rollup_job/rollup_jobs.js | 2 +- yarn.lock | 53 ++++++++++++------- 139 files changed, 183 insertions(+), 179 deletions(-) rename packages/{elastic-datemath => kbn-datemath}/.npmignore (100%) rename packages/{elastic-datemath => kbn-datemath}/BUILD.bazel (95%) rename packages/{elastic-datemath => kbn-datemath}/README.md (61%) rename packages/{elastic-datemath => kbn-datemath}/jest.config.js (94%) rename packages/{elastic-datemath => kbn-datemath}/package.json (77%) rename packages/{elastic-datemath => kbn-datemath}/src/index.test.js (100%) rename packages/{elastic-datemath => kbn-datemath}/src/index.ts (100%) rename packages/{elastic-datemath => kbn-datemath}/tsconfig.json (100%) diff --git a/.eslintrc.js b/.eslintrc.js index 2a4fbe7db3723..b12dc63066370 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -304,8 +304,8 @@ module.exports = { */ { files: [ - 'packages/elastic-datemath/**/*.{js,mjs,ts,tsx}', 'packages/elastic-eslint-config-kibana/**/*.{js,mjs,ts,tsx}', + 'packages/kbn-datemath/**/*.{js,mjs,ts,tsx}', ], rules: { '@kbn/eslint/require-license-header': [ diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 50341621557fa..9c1599a60dd0c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -55,7 +55,7 @@ /examples/field_formats_example/ @elastic/kibana-app-services /examples/partial_results_example/ @elastic/kibana-app-services /examples/search_examples/ @elastic/kibana-app-services -/packages/elastic-datemath/ @elastic/kibana-app-services +/packages/kbn-datemath/ @elastic/kibana-app-services /packages/kbn-interpreter/ @elastic/kibana-app-services /packages/kbn-react-field/ @elastic/kibana-app-services /packages/kbn-es-query/ @elastic/kibana-app-services diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel index 4a2e6ddf76b17..ff9014214d4c0 100644 --- a/WORKSPACE.bazel +++ b/WORKSPACE.bazel @@ -10,15 +10,16 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # Fetch Node.js rules http_archive( name = "build_bazel_rules_nodejs", - sha256 = "8a7c981217239085f78acc9898a1f7ba99af887c1996ceb3b4504655383a2c3c", - urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/4.0.0/rules_nodejs-4.0.0.tar.gz"], + sha256 = "523da2d6b50bc00eaf14b00ed28b1a366b3ab456e14131e9812558b26599125c", + urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/5.3.1/rules_nodejs-5.3.1.tar.gz"], ) -# Now that we have the rules let's import from them to complete the work -load("@build_bazel_rules_nodejs//:index.bzl", "check_rules_nodejs_version", "node_repositories", "yarn_install") +# Build Node.js rules dependencies +load("@build_bazel_rules_nodejs//:repositories.bzl", "build_bazel_rules_nodejs_dependencies") +build_bazel_rules_nodejs_dependencies() -# Assure we have at least a given rules_nodejs version -check_rules_nodejs_version(minimum_version_string = "4.0.0") +# Now that we have the rules let's import from them to complete the work +load("@build_bazel_rules_nodejs//:index.bzl", "node_repositories", "yarn_install") # Setup the Node.js toolchain for the architectures we want to support # @@ -38,14 +39,13 @@ node_repositories( node_urls = [ "https://nodejs.org/dist/v{version}/{filename}", ], - yarn_repositories = { + yarn_releases = { "1.21.1": ("yarn-v1.21.1.tar.gz", "yarn-v1.21.1", "d1d9f4a0f16f5ed484e814afeb98f39b82d4728c6c8beaafb5abc99c02db6674"), }, yarn_version = "1.21.1", yarn_urls = [ "https://github.com/yarnpkg/yarn/releases/download/v{version}/{filename}", ], - package_json = ["//:package.json"], ) # Run yarn_install rule to take care of dependencies diff --git a/docs/developer/getting-started/monorepo-packages.asciidoc b/docs/developer/getting-started/monorepo-packages.asciidoc index e8e792f13c0dd..0216d26023a78 100644 --- a/docs/developer/getting-started/monorepo-packages.asciidoc +++ b/docs/developer/getting-started/monorepo-packages.asciidoc @@ -51,7 +51,6 @@ yarn kbn watch [discrete] === List of Already Migrated Packages to Bazel -- @elastic/datemath - @elastic/eslint-config-kibana - @elastic/safer-lodash-set - @kbn/ace @@ -64,6 +63,7 @@ yarn kbn watch - @kbn/config - @kbn/config-schema - @kbn/crypto +- @kbn/datemath - @kbn/dev-utils - @kbn/docs-utils - @kbn/es diff --git a/package.json b/package.json index 2f159c19ba009..76948decf33a7 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "@elastic/apm-rum-react": "^1.3.4", "@elastic/apm-synthtrace": "link:bazel-bin/packages/elastic-apm-synthtrace", "@elastic/charts": "45.1.1", - "@elastic/datemath": "link:bazel-bin/packages/elastic-datemath", + "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.2.0-canary.2", "@elastic/ems-client": "8.2.0", "@elastic/eui": "53.0.1", @@ -140,6 +140,7 @@ "@kbn/config": "link:bazel-bin/packages/kbn-config", "@kbn/config-schema": "link:bazel-bin/packages/kbn-config-schema", "@kbn/crypto": "link:bazel-bin/packages/kbn-crypto", + "@kbn/datemath": "link:bazel-bin/packages/kbn-datemath", "@kbn/doc-links": "link:bazel-bin/packages/kbn-doc-links", "@kbn/es-query": "link:bazel-bin/packages/kbn-es-query", "@kbn/eslint-plugin-imports": "link:bazel-bin/packages/kbn-eslint-plugin-imports", @@ -455,7 +456,7 @@ "@babel/traverse": "^7.17.3", "@babel/types": "^7.17.0", "@bazel/ibazel": "^0.16.2", - "@bazel/typescript": "4.0.0", + "@bazel/typescript": "5.3.1", "@cypress/code-coverage": "^3.9.12", "@cypress/snapshot": "^2.1.7", "@cypress/webpack-preprocessor": "^5.6.0", @@ -545,7 +546,6 @@ "@types/delete-empty": "^2.0.0", "@types/ejs": "^3.0.6", "@types/elastic__apm-synthtrace": "link:bazel-bin/packages/elastic-apm-synthtrace/npm_module_types", - "@types/elastic__datemath": "link:bazel-bin/packages/elastic-datemath/npm_module_types", "@types/enzyme": "^3.10.8", "@types/eslint": "^7.28.0", "@types/express": "^4.17.13", @@ -595,6 +595,7 @@ "@types/kbn__config": "link:bazel-bin/packages/kbn-config/npm_module_types", "@types/kbn__config-schema": "link:bazel-bin/packages/kbn-config-schema/npm_module_types", "@types/kbn__crypto": "link:bazel-bin/packages/kbn-crypto/npm_module_types", + "@types/kbn__datemath": "link:bazel-bin/packages/kbn-datemath/npm_module_types", "@types/kbn__dev-utils": "link:bazel-bin/packages/kbn-dev-utils/npm_module_types", "@types/kbn__doc-links": "link:bazel-bin/packages/kbn-doc-links/npm_module_types", "@types/kbn__docs-utils": "link:bazel-bin/packages/kbn-docs-utils/npm_module_types", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index ad239f6a180cd..61104d1afcac0 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -11,7 +11,6 @@ filegroup( srcs = [ "//packages/elastic-analytics:build", "//packages/elastic-apm-synthtrace:build", - "//packages/elastic-datemath:build", "//packages/elastic-eslint-config-kibana:build", "//packages/elastic-safer-lodash-set:build", "//packages/kbn-ace:build", @@ -30,6 +29,7 @@ filegroup( "//packages/kbn-config-schema:build", "//packages/kbn-config:build", "//packages/kbn-crypto:build", + "//packages/kbn-datemath:build", "//packages/kbn-dev-utils:build", "//packages/kbn-doc-links:build", "//packages/kbn-docs-utils:build", @@ -102,7 +102,6 @@ filegroup( srcs = [ "//packages/elastic-analytics:build_types", "//packages/elastic-apm-synthtrace:build_types", - "//packages/elastic-datemath:build_types", "//packages/elastic-safer-lodash-set:build_types", "//packages/kbn-ace:build_types", "//packages/kbn-alerts:build_types", @@ -116,6 +115,7 @@ filegroup( "//packages/kbn-config-schema:build_types", "//packages/kbn-config:build_types", "//packages/kbn-crypto:build_types", + "//packages/kbn-datemath:build_types", "//packages/kbn-dev-utils:build_types", "//packages/kbn-doc-links:build_types", "//packages/kbn-docs-utils:build_types", diff --git a/packages/elastic-apm-synthtrace/BUILD.bazel b/packages/elastic-apm-synthtrace/BUILD.bazel index 646b94891f7c2..334d2a0b9e7be 100644 --- a/packages/elastic-apm-synthtrace/BUILD.bazel +++ b/packages/elastic-apm-synthtrace/BUILD.bazel @@ -25,7 +25,7 @@ NPM_MODULE_EXTRA_FILES = [ ] RUNTIME_DEPS = [ - "//packages/elastic-datemath", + "//packages/kbn-datemath", "@npm//@elastic/elasticsearch", "@npm//lodash", "@npm//moment", @@ -36,7 +36,7 @@ RUNTIME_DEPS = [ ] TYPES_DEPS = [ - "//packages/elastic-datemath:npm_module_types", + "//packages/kbn-datemath:npm_module_types", "@npm//@elastic/elasticsearch", "@npm//@types/jest", "@npm//@types/lodash", diff --git a/packages/elastic-apm-synthtrace/src/scripts/run.ts b/packages/elastic-apm-synthtrace/src/scripts/run.ts index 36b3974c12161..ac351eb99ee65 100644 --- a/packages/elastic-apm-synthtrace/src/scripts/run.ts +++ b/packages/elastic-apm-synthtrace/src/scripts/run.ts @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import datemath from '@elastic/datemath'; +import datemath from '@kbn/datemath'; import yargs from 'yargs/yargs'; import { Argv } from 'yargs'; import { intervalToMs } from './utils/interval_to_ms'; diff --git a/packages/elastic-datemath/.npmignore b/packages/kbn-datemath/.npmignore similarity index 100% rename from packages/elastic-datemath/.npmignore rename to packages/kbn-datemath/.npmignore diff --git a/packages/elastic-datemath/BUILD.bazel b/packages/kbn-datemath/BUILD.bazel similarity index 95% rename from packages/elastic-datemath/BUILD.bazel rename to packages/kbn-datemath/BUILD.bazel index 1ee7f8582cb7e..74fbe712eca0e 100644 --- a/packages/elastic-datemath/BUILD.bazel +++ b/packages/kbn-datemath/BUILD.bazel @@ -2,8 +2,8 @@ load("@npm//@bazel/typescript:index.bzl", "ts_config") load("@build_bazel_rules_nodejs//:index.bzl", "js_library") load("//src/dev/bazel:index.bzl", "jsts_transpiler", "ts_project", "pkg_npm", "pkg_npm_types") -PKG_BASE_NAME = "elastic-datemath" -PKG_REQUIRE_NAME = "@elastic/datemath" +PKG_BASE_NAME = "kbn-datemath" +PKG_REQUIRE_NAME = "@kbn/datemath" SOURCE_FILES = glob([ "src/index.ts", diff --git a/packages/elastic-datemath/README.md b/packages/kbn-datemath/README.md similarity index 61% rename from packages/elastic-datemath/README.md rename to packages/kbn-datemath/README.md index a8dcd9f6721cb..3f824c8297d28 100644 --- a/packages/elastic-datemath/README.md +++ b/packages/kbn-datemath/README.md @@ -1,5 +1,5 @@ # datemath -Datemath string parser used in Kibana. This is published to NPM for use in a limited number of locations outside of Kibana, but is not regularly updated and may get seriously out of date. +Datemath string parser used in Kibana. This is published to NPM under (@elastic/datemath) for use in a limited number of locations outside of Kibana, but is not regularly updated and may get seriously out of date. If you file an issue in elastic/kibana we can probably update it for you if needed, though you probably shouldn't depend on this package for anything important. diff --git a/packages/elastic-datemath/jest.config.js b/packages/kbn-datemath/jest.config.js similarity index 94% rename from packages/elastic-datemath/jest.config.js rename to packages/kbn-datemath/jest.config.js index 6a0bd891f1c58..9f3d7546a6fc4 100644 --- a/packages/elastic-datemath/jest.config.js +++ b/packages/kbn-datemath/jest.config.js @@ -20,6 +20,6 @@ module.exports = { preset: '@kbn/test', rootDir: '../..', - roots: ['/packages/elastic-datemath'], + roots: ['/packages/kbn-datemath'], testEnvironment: 'jsdom', }; diff --git a/packages/elastic-datemath/package.json b/packages/kbn-datemath/package.json similarity index 77% rename from packages/elastic-datemath/package.json rename to packages/kbn-datemath/package.json index af958e3730c3d..0bd726afb721e 100644 --- a/packages/elastic-datemath/package.json +++ b/packages/kbn-datemath/package.json @@ -1,6 +1,6 @@ { - "name": "@elastic/datemath", - "version": "5.0.3", + "name": "@kbn/datemath", + "version": "5.0.4", "description": "elasticsearch datemath parser, used in kibana", "license": "Apache-2.0", "main": "./target_node/index.js", diff --git a/packages/elastic-datemath/src/index.test.js b/packages/kbn-datemath/src/index.test.js similarity index 100% rename from packages/elastic-datemath/src/index.test.js rename to packages/kbn-datemath/src/index.test.js diff --git a/packages/elastic-datemath/src/index.ts b/packages/kbn-datemath/src/index.ts similarity index 100% rename from packages/elastic-datemath/src/index.ts rename to packages/kbn-datemath/src/index.ts diff --git a/packages/elastic-datemath/tsconfig.json b/packages/kbn-datemath/tsconfig.json similarity index 100% rename from packages/elastic-datemath/tsconfig.json rename to packages/kbn-datemath/tsconfig.json diff --git a/packages/kbn-docs-utils/src/api_docs/utils.test.ts b/packages/kbn-docs-utils/src/api_docs/utils.test.ts index 488a115554e80..0cfa0331fd202 100644 --- a/packages/kbn-docs-utils/src/api_docs/utils.test.ts +++ b/packages/kbn-docs-utils/src/api_docs/utils.test.ts @@ -21,7 +21,7 @@ const log = new ToolingLog({ }); it('getFileName', () => { - expect(getFileName('@elastic/datemath')).toBe('elastic_datemath'); + expect(getFileName('@kbn/datemath')).toBe('kbn_datemath'); }); it('test getPluginForPath', () => { diff --git a/packages/kbn-eslint-plugin-imports/src/integration_tests/resolve_kibana_import.test.ts b/packages/kbn-eslint-plugin-imports/src/integration_tests/resolve_kibana_import.test.ts index 5610f5b667b75..21b1ea7b30409 100644 --- a/packages/kbn-eslint-plugin-imports/src/integration_tests/resolve_kibana_import.test.ts +++ b/packages/kbn-eslint-plugin-imports/src/integration_tests/resolve_kibana_import.test.ts @@ -63,15 +63,6 @@ describe('standard import formats', () => { }); it('resolves @elastic/ imports', () => { - expect(resolveKibanaImport('@elastic/datemath', pkg('kbn-dev-utils/src'))) - .toMatchInlineSnapshot(` - Object { - "absolute": /node_modules/@elastic/datemath/target_node/index.js, - "nodeModule": "@elastic/datemath", - "type": "file", - } - `); - expect(resolveKibanaImport('@elastic/eui', pkg('kbn-dev-utils/src'))).toMatchInlineSnapshot(` Object { "absolute": /node_modules/@elastic/eui/lib/index.js, diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index a17990291d4d7..0d731fd936507 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -8949,7 +8949,7 @@ const BootstrapCommand = { await time('force install dependencies', async () => { await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_9__["removeYarnIntegrityFileIfExists"])(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(kibanaProjectPath, 'node_modules')); await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_9__["runBazel"])(['clean']); - await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_9__["runBazel"])(['run', '@nodejs//:yarn'], runOffline, { + await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_9__["runBazel"])(['run', '@yarn//:yarn'], runOffline, { env: { SASS_BINARY_SITE: 'https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/node-sass', RE2_DOWNLOAD_MIRROR: 'https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/node-re2' diff --git a/packages/kbn-pm/src/commands/bootstrap.ts b/packages/kbn-pm/src/commands/bootstrap.ts index 51577df6c3b87..691265935b94f 100644 --- a/packages/kbn-pm/src/commands/bootstrap.ts +++ b/packages/kbn-pm/src/commands/bootstrap.ts @@ -72,7 +72,7 @@ export const BootstrapCommand: ICommand = { await time('force install dependencies', async () => { await removeYarnIntegrityFileIfExists(resolve(kibanaProjectPath, 'node_modules')); await runBazel(['clean']); - await runBazel(['run', '@nodejs//:yarn'], runOffline, { + await runBazel(['run', '@yarn//:yarn'], runOffline, { env: { SASS_BINARY_SITE: 'https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/node-sass', diff --git a/packages/kbn-securitysolution-autocomplete/BUILD.bazel b/packages/kbn-securitysolution-autocomplete/BUILD.bazel index 16aa28e79b45b..d8de5ce23ea97 100644 --- a/packages/kbn-securitysolution-autocomplete/BUILD.bazel +++ b/packages/kbn-securitysolution-autocomplete/BUILD.bazel @@ -30,7 +30,7 @@ NPM_MODULE_EXTRA_FILES = [ ] RUNTIME_DEPS = [ - "//packages/elastic-datemath", + "//packages/kbn-datemath", "//packages/kbn-es-query", "//packages/kbn-i18n", "//packages/kbn-securitysolution-io-ts-list-types", @@ -46,7 +46,7 @@ RUNTIME_DEPS = [ ] TYPES_DEPS = [ - "//packages/elastic-datemath:npm_module_types", + "//packages/kbn-datemath:npm_module_types", "//packages/kbn-es-query:npm_module_types", "//packages/kbn-i18n:npm_module_types", "//packages/kbn-securitysolution-io-ts-list-types:npm_module_types", diff --git a/packages/kbn-securitysolution-autocomplete/src/param_is_valid/index.ts b/packages/kbn-securitysolution-autocomplete/src/param_is_valid/index.ts index 016d4f9308e2b..4d9aa734a1841 100644 --- a/packages/kbn-securitysolution-autocomplete/src/param_is_valid/index.ts +++ b/packages/kbn-securitysolution-autocomplete/src/param_is_valid/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { DataViewFieldBase } from '@kbn/es-query'; import { checkEmptyValue } from '../check_empty_value'; diff --git a/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel b/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel index 0229acdb474e4..ce8c3efe8f782 100644 --- a/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel +++ b/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel @@ -28,7 +28,7 @@ NPM_MODULE_EXTRA_FILES = [ ] RUNTIME_DEPS = [ - "//packages/elastic-datemath", + "//packages/kbn-datemath", "@npm//fp-ts", "@npm//io-ts", "@npm//lodash", @@ -37,7 +37,7 @@ RUNTIME_DEPS = [ ] TYPES_DEPS = [ - "//packages/elastic-datemath:npm_module_types", + "//packages/kbn-datemath:npm_module_types", "@npm//fp-ts", "@npm//io-ts", "@npm//moment", diff --git a/packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.ts index d6a99b5fbf880..9d7ef7a74302f 100644 --- a/packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.ts +++ b/packages/kbn-securitysolution-io-ts-utils/src/parse_schedule_dates/index.ts @@ -7,7 +7,7 @@ */ import moment from 'moment'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; export const parseScheduleDates = (time: string): moment.Moment | null => { const isValidDateString = !isNaN(Date.parse(time)); diff --git a/packages/kbn-type-summarizer/BUILD.bazel b/packages/kbn-type-summarizer/BUILD.bazel index ec0df11bc3762..4399e62f11a77 100644 --- a/packages/kbn-type-summarizer/BUILD.bazel +++ b/packages/kbn-type-summarizer/BUILD.bazel @@ -1,8 +1,7 @@ load("@npm//@bazel/typescript:index.bzl", "ts_config") -load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("@rules_nodejs//nodejs:directory_file_path.bzl", "directory_file_path") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "nodejs_binary") load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") -load("@build_bazel_rules_nodejs//internal/node:node.bzl", "nodejs_binary") -load("@build_bazel_rules_nodejs//:index.bzl", "directory_file_path") PKG_BASE_NAME = "kbn-type-summarizer" PKG_REQUIRE_NAME = "@kbn/type-summarizer" diff --git a/packages/kbn-ui-shared-deps-npm/BUILD.bazel b/packages/kbn-ui-shared-deps-npm/BUILD.bazel index 17bbb09bd36e3..ec27891f2592e 100644 --- a/packages/kbn-ui-shared-deps-npm/BUILD.bazel +++ b/packages/kbn-ui-shared-deps-npm/BUILD.bazel @@ -28,7 +28,6 @@ NPM_MODULE_EXTRA_FILES = [ ] RUNTIME_DEPS = [ - "//packages/elastic-datemath", "@npm//@babel/runtime", "@npm//@elastic/charts", "@npm//@elastic/eui", diff --git a/packages/kbn-ui-shared-deps-src/BUILD.bazel b/packages/kbn-ui-shared-deps-src/BUILD.bazel index 295f6fa0594ed..72b02f0f0b160 100644 --- a/packages/kbn-ui-shared-deps-src/BUILD.bazel +++ b/packages/kbn-ui-shared-deps-src/BUILD.bazel @@ -28,10 +28,10 @@ NPM_MODULE_EXTRA_FILES = [ ] RUNTIME_DEPS = [ - "//packages/elastic-datemath", "//packages/elastic-safer-lodash-set", "//packages/kbn-analytics", "//packages/kbn-babel-preset", + "//packages/kbn-datemath", "//packages/kbn-flot-charts", "//packages/kbn-i18n", "//packages/kbn-i18n-react", @@ -42,9 +42,9 @@ RUNTIME_DEPS = [ ] TYPES_DEPS = [ - "//packages/elastic-datemath:npm_module_types", "//packages/elastic-safer-lodash-set:npm_module_types", "//packages/kbn-analytics:npm_module_types", + "//packages/kbn-datemath:npm_module_types", "//packages/kbn-i18n:npm_module_types", "//packages/kbn-i18n-react:npm_module_types", "//packages/kbn-monaco:npm_module_types", diff --git a/packages/kbn-ui-shared-deps-src/src/definitions.js b/packages/kbn-ui-shared-deps-src/src/definitions.js index ce859ca4b977f..ea2bb15c29524 100644 --- a/packages/kbn-ui-shared-deps-src/src/definitions.js +++ b/packages/kbn-ui-shared-deps-src/src/definitions.js @@ -56,6 +56,7 @@ const externals = { numeral: '__kbnSharedDeps__.ElasticNumeral', '@elastic/numeral': '__kbnSharedDeps__.ElasticNumeral', '@elastic/charts': '__kbnSharedDeps__.ElasticCharts', + '@kbn/datemath': '__kbnSharedDeps__.KbnDatemath', '@elastic/eui': '__kbnSharedDeps__.ElasticEui', '@elastic/eui/lib/services': '__kbnSharedDeps__.ElasticEuiLibServices', '@elastic/eui/lib/services/format': '__kbnSharedDeps__.ElasticEuiLibServicesFormat', diff --git a/packages/kbn-ui-shared-deps-src/src/entry.js b/packages/kbn-ui-shared-deps-src/src/entry.js index a52dcc1440efb..d88de271519bd 100644 --- a/packages/kbn-ui-shared-deps-src/src/entry.js +++ b/packages/kbn-ui-shared-deps-src/src/entry.js @@ -41,7 +41,7 @@ export const ElasticEui = require('@elastic/eui'); export const ElasticEuiLibServices = require('@elastic/eui/lib/services'); export const ElasticEuiLibServicesFormat = require('@elastic/eui/lib/services/format'); export const ElasticEuiChartsTheme = require('@elastic/eui/dist/eui_charts_theme'); -export const ElasticDatemath = require('@elastic/datemath'); +export const KbnDatemath = require('@kbn/datemath'); export const ReactBeautifulDnD = require('react-beautiful-dnd'); export const Lodash = require('lodash'); diff --git a/src/dev/bazel/pkg_npm_types.bzl b/src/dev/bazel/pkg_npm_types.bzl index e5caba5149053..f90d60252af2d 100644 --- a/src/dev/bazel/pkg_npm_types.bzl +++ b/src/dev/bazel/pkg_npm_types.bzl @@ -142,7 +142,7 @@ pkg_npm_types = rule( "_packager": attr.label( doc = "Target that executes the npm types package assembler binary", executable = True, - cfg = "host", + cfg = "exec", default = Label("//packages/kbn-type-summarizer:bazel-cli"), ), }, diff --git a/src/plugins/controls/public/services/kibana/options_list.ts b/src/plugins/controls/public/services/kibana/options_list.ts index eb805ec4dbe02..a731b0acca62b 100644 --- a/src/plugins/controls/public/services/kibana/options_list.ts +++ b/src/plugins/controls/public/services/kibana/options_list.ts @@ -7,7 +7,7 @@ */ import { memoize } from 'lodash'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { buildEsQuery } from '@kbn/es-query'; import { TimeRange } from '../../../../data/public'; diff --git a/src/plugins/data/common/query/timefilter/get_time.ts b/src/plugins/data/common/query/timefilter/get_time.ts index fd21b2251ea3a..287b0f3de21e7 100644 --- a/src/plugins/data/common/query/timefilter/get_time.ts +++ b/src/plugins/data/common/query/timefilter/get_time.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { omitBy } from 'lodash'; import { buildRangeFilter } from '@kbn/es-query'; import type { Moment } from 'moment'; diff --git a/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_es_interval.ts b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_es_interval.ts index 4b3ec90bb2cc2..9b2ed50fb2459 100644 --- a/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_es_interval.ts +++ b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_es_interval.ts @@ -7,7 +7,7 @@ */ import moment from 'moment'; -import dateMath, { Unit } from '@elastic/datemath'; +import dateMath, { Unit } from '@kbn/datemath'; import { parseEsInterval } from '../../../utils'; diff --git a/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts index 12c6d39ad905a..f8a89a8e33e46 100644 --- a/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts +++ b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts @@ -9,7 +9,7 @@ import { Assign } from 'utility-types'; import { isString, isObject as isObjectLodash, isPlainObject, sortBy } from 'lodash'; import moment, { Moment } from 'moment'; -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import { parseInterval, splitStringInterval } from '../../../utils'; import { TimeRangeBounds } from '../../../../../query'; import { calcAutoIntervalLessThan, calcAutoIntervalNear } from './calc_auto_interval'; diff --git a/src/plugins/data/common/search/aggs/utils/date_interval_utils/invalid_es_calendar_interval_error.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/invalid_es_calendar_interval_error.ts index 3ed68d876c664..ea259fbf6239d 100644 --- a/src/plugins/data/common/search/aggs/utils/date_interval_utils/invalid_es_calendar_interval_error.ts +++ b/src/plugins/data/common/search/aggs/utils/date_interval_utils/invalid_es_calendar_interval_error.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import { i18n } from '@kbn/i18n'; export class InvalidEsCalendarIntervalError extends Error { diff --git a/src/plugins/data/common/search/aggs/utils/date_interval_utils/least_common_interval.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/least_common_interval.ts index ff895a976ac64..0e5fc49fb1d29 100644 --- a/src/plugins/data/common/search/aggs/utils/date_interval_utils/least_common_interval.ts +++ b/src/plugins/data/common/search/aggs/utils/date_interval_utils/least_common_interval.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { leastCommonMultiple } from './least_common_multiple'; import { parseEsInterval } from './parse_es_interval'; diff --git a/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_es_interval.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_es_interval.ts index b723c3f45c5a6..237a509277336 100644 --- a/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_es_interval.ts +++ b/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_es_interval.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import dateMath, { Unit } from '@elastic/datemath'; +import dateMath, { Unit } from '@kbn/datemath'; import { InvalidEsCalendarIntervalError } from './invalid_es_calendar_interval_error'; import { InvalidEsIntervalFormatError } from './invalid_es_interval_format_error'; diff --git a/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_interval.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_interval.ts index e6ce43526306d..113abfb056f70 100644 --- a/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_interval.ts +++ b/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_interval.ts @@ -8,7 +8,7 @@ import { find } from 'lodash'; import moment from 'moment'; -import dateMath, { Unit } from '@elastic/datemath'; +import dateMath, { Unit } from '@kbn/datemath'; // Assume interval is in the form (value)(unit), such as "1h" const INTERVAL_STRING_RE = new RegExp('^([0-9\\.]*)\\s*(' + dateMath.units.join('|') + ')$'); diff --git a/src/plugins/data/common/search/aggs/utils/date_interval_utils/to_absolute_dates.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/to_absolute_dates.ts index 03b6bcf83c642..d147caf92711f 100644 --- a/src/plugins/data/common/search/aggs/utils/date_interval_utils/to_absolute_dates.ts +++ b/src/plugins/data/common/search/aggs/utils/date_interval_utils/to_absolute_dates.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { TimeRange } from '../../../../../common'; export function toAbsoluteDates(range: TimeRange) { diff --git a/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts b/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts index f88fd3b9a0157..0caf2d4ced530 100644 --- a/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts +++ b/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { buildQueryFromFilters } from '@kbn/es-query'; import { memoize } from 'lodash'; import { CoreSetup } from 'src/core/public'; diff --git a/src/plugins/data/public/query/timefilter/lib/validate_timerange.ts b/src/plugins/data/public/query/timefilter/lib/validate_timerange.ts index 12271f8388ca5..e9eb97d004c91 100644 --- a/src/plugins/data/public/query/timefilter/lib/validate_timerange.ts +++ b/src/plugins/data/public/query/timefilter/lib/validate_timerange.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { TimeRange } from '../../../../common'; export function validateTimeRange(time?: TimeRange): boolean { diff --git a/src/plugins/discover/public/application/main/components/chart/histogram.tsx b/src/plugins/discover/public/application/main/components/chart/histogram.tsx index 22eff35be2325..2a71207e82835 100644 --- a/src/plugins/discover/public/application/main/components/chart/histogram.tsx +++ b/src/plugins/discover/public/application/main/components/chart/histogram.tsx @@ -18,7 +18,7 @@ import { EuiText, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { Axis, BrushEndListener, diff --git a/src/plugins/discover/public/application/main/components/chart/point_series.test.ts b/src/plugins/discover/public/application/main/components/chart/point_series.test.ts index 37fa894f7f671..3497bc54b4a35 100644 --- a/src/plugins/discover/public/application/main/components/chart/point_series.test.ts +++ b/src/plugins/discover/public/application/main/components/chart/point_series.test.ts @@ -8,7 +8,7 @@ import { buildPointSeriesData } from './point_series'; import moment from 'moment'; -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; describe('buildPointSeriesData', () => { test('with valid data', () => { diff --git a/src/plugins/discover/public/application/main/components/chart/point_series.ts b/src/plugins/discover/public/application/main/components/chart/point_series.ts index b8cdf75d5fd36..88b5c444f4beb 100644 --- a/src/plugins/discover/public/application/main/components/chart/point_series.ts +++ b/src/plugins/discover/public/application/main/components/chart/point_series.ts @@ -8,7 +8,7 @@ import { uniq } from 'lodash'; import { Duration, Moment } from 'moment'; -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import { SerializedFieldFormat } from '../../../../../../field_formats/common'; export interface Column { diff --git a/src/plugins/discover/public/application/main/utils/get_dimensions.ts b/src/plugins/discover/public/application/main/utils/get_dimensions.ts index 1c8bc70bb17e2..c9a2a198d1f7e 100644 --- a/src/plugins/discover/public/application/main/utils/get_dimensions.ts +++ b/src/plugins/discover/public/application/main/utils/get_dimensions.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ import moment from 'moment'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { DataPublicPluginStart, search, IAggConfigs } from '../../../../../data/public'; import { Dimensions, HistogramParamsBounds } from '../components/chart/point_series'; diff --git a/src/plugins/discover/public/application/main/utils/validate_time_range.ts b/src/plugins/discover/public/application/main/utils/validate_time_range.ts index e506fe6fdb898..7744a10f3e8ca 100644 --- a/src/plugins/discover/public/application/main/utils/validate_time_range.ts +++ b/src/plugins/discover/public/application/main/utils/validate_time_range.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { i18n } from '@kbn/i18n'; import { ToastsStart } from 'kibana/public'; diff --git a/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts b/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts index 3b8fb867c361b..eb67d630f2393 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts +++ b/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { Filter, FieldFilter } from '@kbn/es-query'; import { ES_FIELD_TYPES } from '@kbn/field-types'; import isSemverValid from 'semver/functions/valid'; diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index ddba035b91fdd..1589c9c86f1df 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import classNames from 'classnames'; import React, { useCallback, useMemo, useRef, useState } from 'react'; import deepEqual from 'fast-deep-equal'; diff --git a/src/plugins/vis_default_editor/public/components/controls/date_ranges.tsx b/src/plugins/vis_default_editor/public/components/controls/date_ranges.tsx index 22907ff16e24d..f060cfb311023 100644 --- a/src/plugins/vis_default_editor/public/components/controls/date_ranges.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/date_ranges.tsx @@ -21,7 +21,7 @@ import { EuiText, EuiFormRow, } from '@elastic/eui'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { isEqual, omit } from 'lodash'; diff --git a/src/plugins/vis_types/timeseries/common/interval_regexp.ts b/src/plugins/vis_types/timeseries/common/interval_regexp.ts index 7e190ab6e8d2f..80aea32f0feb3 100644 --- a/src/plugins/vis_types/timeseries/common/interval_regexp.ts +++ b/src/plugins/vis_types/timeseries/common/interval_regexp.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; export const GTE_INTERVAL_RE = new RegExp(`^>=([\\d\\.]+\\s*(${dateMath.units.join('|')}))$`); export const INTERVAL_STRING_RE = new RegExp(`^([\\d\\.]+)\\s*(${dateMath.units.join('|')})$`); diff --git a/src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.ts index c841f08895b7a..b630427781179 100644 --- a/src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import { convertIntervalToUnit, parseInterval, diff --git a/src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.test.ts index e1cc1f1f26eb2..9e48b306445b2 100644 --- a/src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.test.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import type { Panel } from '../../../../common/types'; import { RollupSearchCapabilities } from './rollup_search_capabilities'; diff --git a/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/interval_helper.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/interval_helper.ts index 1468d5ddee5bf..34d0824df7fca 100644 --- a/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/interval_helper.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/interval_helper.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; export type Unit = 'ms' | 's' | 'm' | 'h' | 'd' | 'w' | 'M' | 'y'; diff --git a/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_bucket_size.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_bucket_size.ts index e02b403c8ba13..58c96eca99b61 100644 --- a/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_bucket_size.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_bucket_size.ts @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import type { Unit } from '@elastic/datemath'; +import type { Unit } from '@kbn/datemath'; import { getUnitValue, diff --git a/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/parse_interval.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/parse_interval.js index a2dd549fd828f..9e9b866c7b98e 100644 --- a/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/parse_interval.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/parse_interval.js @@ -8,7 +8,7 @@ import _ from 'lodash'; import moment from 'moment'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; // Assume interval is in the form (value)(unit), such as "1h" const INTERVAL_STRING_RE = new RegExp('^([0-9\\.]*)\\s*(' + dateMath.units.join('|') + ')$'); diff --git a/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/unit_to_seconds.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/unit_to_seconds.test.ts index 64999154c10fb..356df6cd86f5e 100644 --- a/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/unit_to_seconds.test.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/unit_to_seconds.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import { getUnitValue, diff --git a/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/unit_to_seconds.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/unit_to_seconds.ts index b3a29287208d6..3252736669bce 100644 --- a/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/unit_to_seconds.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/unit_to_seconds.ts @@ -7,7 +7,7 @@ */ import { sortBy, isNumber } from 'lodash'; -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; /** @ts-ignore */ import { INTERVAL_STRING_RE } from '../../../../common/interval_regexp'; diff --git a/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js b/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js index de2e0f57a111b..e518894c5511a 100644 --- a/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js +++ b/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js @@ -8,7 +8,7 @@ import $ from 'jquery'; import moment from 'moment'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { scheme, loader, logger, Warn, version as vegaVersion, expressionFunction } from 'vega'; import { expressionInterpreter } from 'vega-interpreter'; import { version as vegaLiteVersion } from 'vega-lite'; diff --git a/src/plugins/vis_types/vislib/public/vislib/visualizations/time_marker.js b/src/plugins/vis_types/vislib/public/vislib/visualizations/time_marker.js index a0a4fce8d7af4..83055f4a4c5a0 100644 --- a/src/plugins/vis_types/vislib/public/vislib/visualizations/time_marker.js +++ b/src/plugins/vis_types/vislib/public/vislib/visualizations/time_marker.js @@ -7,7 +7,7 @@ */ import d3 from 'd3'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; export class TimeMarker { constructor(times, xScale, height) { diff --git a/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/save_modal.tsx b/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/save_modal.tsx index 9ea40fdd38a36..291cde98fd58f 100644 --- a/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/save_modal.tsx +++ b/x-pack/plugins/apm/public/components/app/service_groups/service_group_save/save_modal.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import datemath from '@elastic/datemath'; +import datemath from '@kbn/datemath'; import { EuiModal } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useHistory } from 'react-router-dom'; diff --git a/x-pack/plugins/apm/public/context/url_params_context/helpers.test.ts b/x-pack/plugins/apm/public/context/url_params_context/helpers.test.ts index 9ab0948fd75aa..9ed05a1ef186e 100644 --- a/x-pack/plugins/apm/public/context/url_params_context/helpers.test.ts +++ b/x-pack/plugins/apm/public/context/url_params_context/helpers.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import datemath from '@elastic/datemath'; +import datemath from '@kbn/datemath'; import moment from 'moment-timezone'; import * as helpers from './helpers'; diff --git a/x-pack/plugins/apm/public/context/url_params_context/helpers.ts b/x-pack/plugins/apm/public/context/url_params_context/helpers.ts index 6856c0e7d2506..1fb70e929ad5a 100644 --- a/x-pack/plugins/apm/public/context/url_params_context/helpers.ts +++ b/x-pack/plugins/apm/public/context/url_params_context/helpers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import datemath from '@elastic/datemath'; +import datemath from '@kbn/datemath'; import { pickBy } from 'lodash'; import { UrlParams } from './types'; diff --git a/x-pack/plugins/apm/server/routes/alerts/register_anomaly_alert_type.ts b/x-pack/plugins/apm/server/routes/alerts/register_anomaly_alert_type.ts index 5affecb3541cc..f84fe4d3169fb 100644 --- a/x-pack/plugins/apm/server/routes/alerts/register_anomaly_alert_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/register_anomaly_alert_type.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import datemath from '@elastic/datemath'; +import datemath from '@kbn/datemath'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { schema } from '@kbn/config-schema'; import { diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/timefilter.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/timefilter.ts index b61e03319b916..751dbf5126750 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/timefilter.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/timefilter.ts @@ -5,7 +5,7 @@ * 2.0. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { ExpressionValueFilter, ExpressionFunctionDefinition } from '../../../types'; import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; diff --git a/x-pack/plugins/canvas/common/lib/build_embeddable_filters.ts b/x-pack/plugins/canvas/common/lib/build_embeddable_filters.ts index c98d2f080452a..9209eec105b23 100644 --- a/x-pack/plugins/canvas/common/lib/build_embeddable_filters.ts +++ b/x-pack/plugins/canvas/common/lib/build_embeddable_filters.ts @@ -6,7 +6,7 @@ */ import { buildQueryFilter, Filter } from '@kbn/es-query'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { maxBy, minBy } from 'lodash'; import { ExpressionValueFilter } from '../../types'; // @ts-expect-error untyped local diff --git a/x-pack/plugins/canvas/public/components/workpad_filters/filter_views/time_filter.ts b/x-pack/plugins/canvas/public/components/workpad_filters/filter_views/time_filter.ts index 1dc02f61d05f7..afdd1d89d3690 100644 --- a/x-pack/plugins/canvas/public/components/workpad_filters/filter_views/time_filter.ts +++ b/x-pack/plugins/canvas/public/components/workpad_filters/filter_views/time_filter.ts @@ -5,7 +5,7 @@ * 2.0. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { i18n } from '@kbn/i18n'; import { FilterType, FilterViewSpec, SimpleFilterViewField } from '../../../../types'; import { formatByKey } from '../utils'; diff --git a/x-pack/plugins/data_visualizer/common/services/time_buckets.js b/x-pack/plugins/data_visualizer/common/services/time_buckets.js index 49de535ee6c26..92b60cbf6ca10 100644 --- a/x-pack/plugins/data_visualizer/common/services/time_buckets.js +++ b/x-pack/plugins/data_visualizer/common/services/time_buckets.js @@ -9,7 +9,7 @@ import { FIELD_FORMAT_IDS } from '../../../../../src/plugins/field_formats/commo import { UI_SETTINGS } from '../../../../../src/plugins/data/common'; import { ary, assign, isPlainObject, isString, sortBy } from 'lodash'; import moment from 'moment'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { parseInterval } from '../utils/parse_interval'; const { duration: d } = moment; diff --git a/x-pack/plugins/data_visualizer/common/utils/parse_interval.ts b/x-pack/plugins/data_visualizer/common/utils/parse_interval.ts index 6ca280dc12ebd..73a20974cde4f 100644 --- a/x-pack/plugins/data_visualizer/common/utils/parse_interval.ts +++ b/x-pack/plugins/data_visualizer/common/utils/parse_interval.ts @@ -6,7 +6,7 @@ */ import { duration, Duration, unitOfTime } from 'moment'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; type SupportedUnits = unitOfTime.Base; diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/full_time_range_selector/full_time_range_selector_service.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/full_time_range_selector/full_time_range_selector_service.ts index 5f20f02b96c7e..3cc3fa329359d 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/full_time_range_selector/full_time_range_selector_service.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/full_time_range_selector/full_time_range_selector_service.ts @@ -7,7 +7,7 @@ import moment from 'moment'; import { TimefilterContract } from 'src/plugins/data/public'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { i18n } from '@kbn/i18n'; import type { ToastsStart } from 'kibana/public'; diff --git a/x-pack/plugins/infra/public/components/infrastructure_node_metrics_tables/shared/components/metrics_node_details_link.tsx b/x-pack/plugins/infra/public/components/infrastructure_node_metrics_tables/shared/components/metrics_node_details_link.tsx index b51e1bc8b7707..cb83afbc10e3f 100644 --- a/x-pack/plugins/infra/public/components/infrastructure_node_metrics_tables/shared/components/metrics_node_details_link.tsx +++ b/x-pack/plugins/infra/public/components/infrastructure_node_metrics_tables/shared/components/metrics_node_details_link.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { parse } from '@elastic/datemath'; +import { parse } from '@kbn/datemath'; import { EuiLink } from '@elastic/eui'; import React from 'react'; import { useLinkProps } from '../../../../../../observability/public'; diff --git a/x-pack/plugins/infra/public/components/infrastructure_node_metrics_tables/shared/hooks/use_infrastructure_node_metrics.ts b/x-pack/plugins/infra/public/components/infrastructure_node_metrics_tables/shared/hooks/use_infrastructure_node_metrics.ts index 47e4fd86f04e2..f3daa1b429aca 100644 --- a/x-pack/plugins/infra/public/components/infrastructure_node_metrics_tables/shared/hooks/use_infrastructure_node_metrics.ts +++ b/x-pack/plugins/infra/public/components/infrastructure_node_metrics_tables/shared/hooks/use_infrastructure_node_metrics.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { parse } from '@elastic/datemath'; +import { parse } from '@kbn/datemath'; import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { useEffect, useMemo, useState } from 'react'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index 316249ec0fe3c..f6e3ff27f2467 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -15,7 +15,7 @@ import { } from '@elastic/eui'; import { FormattedMessage, FormattedTime, FormattedRelative } from '@kbn/i18n-react'; import * as React from 'react'; -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { LogTextSeparator } from './log_text_separator'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx index bba95b0fffb05..34faac3aefa0c 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import datemath from '@elastic/datemath'; +import datemath from '@kbn/datemath'; import { EuiFlexGroup, EuiFlexItem, EuiPage, EuiSuperDatePicker } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results_url_state.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results_url_state.tsx index a845e59ce6d32..73d0046ce9643 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results_url_state.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results_url_state.tsx @@ -7,7 +7,7 @@ import { useCallback, useMemo, useState } from 'react'; import useInterval from 'react-use/lib/useInterval'; -import datemath from '@elastic/datemath'; +import datemath from '@kbn/datemath'; import moment from 'moment'; import * as rt from 'io-ts'; import { TimeRange as KibanaTimeRange } from '../../../../../../../src/plugins/data/public'; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_time.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_time.ts index ada9b0d468fdc..5bbc55dd0f48e 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_time.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_time.ts @@ -9,7 +9,7 @@ import { useCallback, useState, useEffect } from 'react'; import * as rt from 'io-ts'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; -import DateMath from '@elastic/datemath'; +import DateMath from '@kbn/datemath'; import { constant, identity } from 'fp-ts/lib/function'; import createContainer from 'constate'; import { useUrlState } from '../../../../utils/use_url_state'; diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx index 8da96586be979..cf766bdb104e5 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx @@ -6,7 +6,7 @@ */ import React, { useCallback, useEffect, useState, useContext } from 'react'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import moment from 'moment'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { Source } from '../../../../containers/metrics_source'; diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/use_metrics_time.ts b/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/use_metrics_time.ts index 80af26401c334..98e54daadcade 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/use_metrics_time.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/use_metrics_time.ts @@ -8,7 +8,7 @@ import createContainer from 'constate'; import { useState, useCallback, useEffect } from 'react'; import moment from 'moment'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import * as rt from 'io-ts'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx index 461d0defb000a..9a2b8366f920f 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx @@ -14,7 +14,7 @@ import { EuiContextMenuPanelDescriptor, EuiPopover, } from '@elastic/eui'; -import DateMath from '@elastic/datemath'; +import DateMath from '@kbn/datemath'; import { Capabilities } from 'src/core/public'; import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources'; import { AlertFlyout } from '../../../../alerting/metric_threshold/components/alert_flyout'; diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts index 788760a0dfe1c..ab4bf11dadb35 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts @@ -5,7 +5,7 @@ * 2.0. */ -import DateMath from '@elastic/datemath'; +import DateMath from '@kbn/datemath'; import { useEffect, useState } from 'react'; import { DataViewBase } from '@kbn/es-query'; import { isEqual } from 'lodash'; diff --git a/x-pack/plugins/infra/public/utils/convert_interval_to_string.ts b/x-pack/plugins/infra/public/utils/convert_interval_to_string.ts index 075535993e09c..7435e4264a2b2 100644 --- a/x-pack/plugins/infra/public/utils/convert_interval_to_string.ts +++ b/x-pack/plugins/infra/public/utils/convert_interval_to_string.ts @@ -5,7 +5,7 @@ * 2.0. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import moment from 'moment'; import { i18n } from '@kbn/i18n'; import * as rt from 'io-ts'; diff --git a/x-pack/plugins/infra/public/utils/datemath.ts b/x-pack/plugins/infra/public/utils/datemath.ts index fa67c86544fb0..2ed4f68b7a934 100644 --- a/x-pack/plugins/infra/public/utils/datemath.ts +++ b/x-pack/plugins/infra/public/utils/datemath.ts @@ -5,7 +5,7 @@ * 2.0. */ -import dateMath, { Unit } from '@elastic/datemath'; +import dateMath, { Unit } from '@kbn/datemath'; const JS_MAX_DATE = 8640000000000000; diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_anomaly/preview_metric_anomaly_alert.ts b/x-pack/plugins/infra/server/lib/alerting/metric_anomaly/preview_metric_anomaly_alert.ts index b5033bb9a6043..e10558be54033 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_anomaly/preview_metric_anomaly_alert.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_anomaly/preview_metric_anomaly_alert.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import { countBy } from 'lodash'; import { MappedAnomalyHit } from '../../infra_ml'; import { MlSystem, MlAnomalyDetectors } from '../../../types'; diff --git a/x-pack/plugins/infra/server/lib/metrics/lib/calculate_bucket_size/interval_regex.ts b/x-pack/plugins/infra/server/lib/metrics/lib/calculate_bucket_size/interval_regex.ts index 20657880c497e..c53119cb14299 100644 --- a/x-pack/plugins/infra/server/lib/metrics/lib/calculate_bucket_size/interval_regex.ts +++ b/x-pack/plugins/infra/server/lib/metrics/lib/calculate_bucket_size/interval_regex.ts @@ -5,7 +5,7 @@ * 2.0. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; export const GTE_INTERVAL_RE = new RegExp(`^>=([\\d\\.]+\\s*(${dateMath.units.join('|')}))$`); export const INTERVAL_STRING_RE = new RegExp(`^([\\d\\.]+)\\s*(${dateMath.units.join('|')})$`); diff --git a/x-pack/plugins/infra/server/utils/round_timestamp.ts b/x-pack/plugins/infra/server/utils/round_timestamp.ts index e15fceec5120f..cd2234ed9eef7 100644 --- a/x-pack/plugins/infra/server/utils/round_timestamp.ts +++ b/x-pack/plugins/infra/server/utils/round_timestamp.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import moment from 'moment'; export const roundTimestamp = (timestamp: number, unit: Unit) => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx index 7a391b1639a58..82b4a0aa33409 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx @@ -8,7 +8,7 @@ import './field_item.scss'; import React, { useCallback, useState, useMemo } from 'react'; -import DateMath from '@elastic/datemath'; +import DateMath from '@kbn/datemath'; import { EuiButtonGroup, EuiButtonIcon, diff --git a/x-pack/plugins/lens/server/routes/field_stats.ts b/x-pack/plugins/lens/server/routes/field_stats.ts index 787e21b2dd9d9..2a719957cd851 100644 --- a/x-pack/plugins/lens/server/routes/field_stats.ts +++ b/x-pack/plugins/lens/server/routes/field_stats.ts @@ -6,7 +6,7 @@ */ import { errors } from '@elastic/elasticsearch'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import DateMath from '@elastic/datemath'; +import DateMath from '@kbn/datemath'; import { schema } from '@kbn/config-schema'; import { CoreSetup } from 'src/core/server'; import type { DataViewField } from 'src/plugins/data_views/common'; diff --git a/x-pack/plugins/ml/common/util/date_utils.ts b/x-pack/plugins/ml/common/util/date_utils.ts index d09c0bbe0ca39..c668eda03a7be 100644 --- a/x-pack/plugins/ml/common/util/date_utils.ts +++ b/x-pack/plugins/ml/common/util/date_utils.ts @@ -7,7 +7,7 @@ // utility functions for handling dates -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { formatDate } from '@elastic/eui'; import { TimeRange } from '../../../../../src/plugins/data/common'; import { TIME_FORMAT } from '../constants/time_format'; diff --git a/x-pack/plugins/ml/common/util/parse_interval.ts b/x-pack/plugins/ml/common/util/parse_interval.ts index 6ca280dc12ebd..73a20974cde4f 100644 --- a/x-pack/plugins/ml/common/util/parse_interval.ts +++ b/x-pack/plugins/ml/common/util/parse_interval.ts @@ -6,7 +6,7 @@ */ import { duration, Duration, unitOfTime } from 'moment'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; type SupportedUnits = unitOfTime.Base; diff --git a/x-pack/plugins/ml/public/application/components/full_time_range_selector/full_time_range_selector_service.ts b/x-pack/plugins/ml/public/application/components/full_time_range_selector/full_time_range_selector_service.ts index 7e14639f1b8b4..b4fecef196ef0 100644 --- a/x-pack/plugins/ml/public/application/components/full_time_range_selector/full_time_range_selector_service.ts +++ b/x-pack/plugins/ml/public/application/components/full_time_range_selector/full_time_range_selector_service.ts @@ -8,7 +8,7 @@ import moment from 'moment'; import { i18n } from '@kbn/i18n'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { getTimefilter, getToastNotifications } from '../../util/dependency_cache'; import { ml, GetTimeFieldRangeResponse } from '../../services/ml_api_service'; diff --git a/x-pack/plugins/ml/public/application/util/chart_utils.test.js b/x-pack/plugins/ml/public/application/util/chart_utils.test.js index 0900bfacd354e..2c457b079542a 100644 --- a/x-pack/plugins/ml/public/application/util/chart_utils.test.js +++ b/x-pack/plugins/ml/public/application/util/chart_utils.test.js @@ -8,7 +8,7 @@ import seriesConfig from '../explorer/explorer_charts/__mocks__/mock_series_config_filebeat'; jest.mock('./dependency_cache', () => { - const dateMath = require('@elastic/datemath'); + const dateMath = require('@kbn/datemath'); let _time = undefined; const timefilter = { setTime: (time) => { diff --git a/x-pack/plugins/ml/public/application/util/time_buckets.js b/x-pack/plugins/ml/public/application/util/time_buckets.js index d9cfa7729d8d8..21a991ce0a190 100644 --- a/x-pack/plugins/ml/public/application/util/time_buckets.js +++ b/x-pack/plugins/ml/public/application/util/time_buckets.js @@ -7,7 +7,7 @@ import { isPlainObject, isString, ary, sortBy, assign } from 'lodash'; import moment from 'moment'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { timeBucketsCalcAutoIntervalProvider } from './calc_auto_interval'; import { parseInterval } from '../../../common/util/parse_interval'; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/date_range_picker.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/date_range_picker.tsx index d4a060359b146..200c332d7aa1d 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/date_range_picker.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/date_range_picker.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { EuiDatePicker, EuiDatePickerRange } from '@elastic/eui'; import { Moment } from 'moment'; -import DateMath from '@elastic/datemath'; +import DateMath from '@kbn/datemath'; import { i18n } from '@kbn/i18n'; import { useSeriesStorage } from '../hooks/use_series_storage'; import { useUiSetting } from '../../../../../../../../src/plugins/kibana_react/public'; diff --git a/x-pack/plugins/observability/public/utils/date.ts b/x-pack/plugins/observability/public/utils/date.ts index b694bd61d39a9..5839adf92618b 100644 --- a/x-pack/plugins/observability/public/utils/date.ts +++ b/x-pack/plugins/observability/public/utils/date.ts @@ -5,7 +5,7 @@ * 2.0. */ -import datemath from '@elastic/datemath'; +import datemath from '@kbn/datemath'; export function getAbsoluteTime(range: string, opts: Parameters[1] = {}) { const parsed = datemath.parse(range, opts); diff --git a/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.tsx b/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.tsx index dc2ba963972a1..879a735b33004 100644 --- a/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { EuiSuperDatePicker, OnRefreshChangeProps, diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/normalize_time_range.test.ts b/x-pack/plugins/security_solution/public/common/components/url_state/normalize_time_range.test.ts index a87b1206c0082..c5c18a10922a1 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/normalize_time_range.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/normalize_time_range.test.ts @@ -13,7 +13,7 @@ import { RelativeTimeRange, isRelativeTimeRange, } from '../../store/inputs/model'; -import DateMath from '@elastic/datemath'; +import DateMath from '@kbn/datemath'; import { getTimeRangeSettings } from '../../utils/default_date_settings'; const getTimeRangeSettingsMock = getTimeRangeSettings as jest.Mock; diff --git a/x-pack/plugins/security_solution/public/common/hooks/eql/helpers.ts b/x-pack/plugins/security_solution/public/common/hooks/eql/helpers.ts index 7594696336eda..63c1dd7f2d927 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/eql/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/eql/helpers.ts @@ -6,7 +6,7 @@ */ import moment from 'moment'; -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import { inputsModel } from '../../../common/store'; import type { EqlSearchStrategyResponse } from '../../../../../../../src/plugins/data/common'; diff --git a/x-pack/plugins/security_solution/public/common/hooks/eql/types.ts b/x-pack/plugins/security_solution/public/common/hooks/eql/types.ts index 4bf5aa73863df..06fb0e1c72b3e 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/eql/types.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/eql/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import { InspectResponse } from '../../../types'; import { ChartData } from '../../components/charts/common'; diff --git a/x-pack/plugins/security_solution/public/common/hooks/eql/use_eql_preview.test.ts b/x-pack/plugins/security_solution/public/common/hooks/eql/use_eql_preview.test.ts index 00edace90b7fd..36246cc602f11 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/eql/use_eql_preview.test.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/eql/use_eql_preview.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import { renderHook, act } from '@testing-library/react-hooks'; import { of, throwError } from 'rxjs'; import { delay } from 'rxjs/operators'; diff --git a/x-pack/plugins/security_solution/public/common/utils/default_date_settings.ts b/x-pack/plugins/security_solution/public/common/utils/default_date_settings.ts index ed747dbcf22bb..3743560698378 100644 --- a/x-pack/plugins/security_solution/public/common/utils/default_date_settings.ts +++ b/x-pack/plugins/security_solution/public/common/utils/default_date_settings.ts @@ -5,7 +5,7 @@ * 2.0. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import moment from 'moment'; import { isBoolean, isNumber, isString } from 'lodash/fp'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx index 95042ac776875..1c3c010c972f7 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx @@ -10,7 +10,7 @@ import { getOr, isEmpty } from 'lodash/fp'; import moment from 'moment'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { FilterStateStore, Filter } from '@kbn/es-query'; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/helpers.ts b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/helpers.ts index b57af8ede2fef..f4e58c0d34c74 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/helpers.ts @@ -8,7 +8,7 @@ import { Position, ScaleType } from '@elastic/charts'; import { EuiSelectOption } from '@elastic/eui'; import { Type, Language, ThreatMapping } from '@kbn/securitysolution-io-ts-alerting-types'; -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import type { Filter } from '@kbn/es-query'; import * as i18n from './translations'; import { histogramDateTimeFormatter } from '../../../../common/components/utils'; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/index.tsx index 2dac0b68b0299..a870b837a7d33 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/index.tsx @@ -6,7 +6,7 @@ */ import React, { useState, useEffect, useMemo } from 'react'; -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import { ThreatMapping, Type } from '@kbn/securitysolution-io-ts-alerting-types'; import styled from 'styled-components'; import { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.tsx index 3b640592535b6..326811783fc4a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.tsx @@ -7,7 +7,7 @@ import React, { useEffect, useMemo } from 'react'; import usePrevious from 'react-use/lib/usePrevious'; -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import { EuiFlexGroup, EuiFlexItem, EuiText, EuiSpacer, EuiLoadingChart } from '@elastic/eui'; import styled from 'styled-components'; import { Type } from '@kbn/securitysolution-io-ts-alerting-types'; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_route.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_route.tsx index d5278144cf70a..4e05327d74981 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_route.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_route.tsx @@ -6,7 +6,7 @@ */ import { useEffect, useState, useCallback } from 'react'; -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import { Type, ThreatMapping } from '@kbn/securitysolution-io-ts-alerting-types'; import { FieldValueQueryBar } from '../query_bar'; import { usePreviewRule } from '../../../containers/detection_engine/rules/use_preview_rule'; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts index a75558b8a8a1a..7223e11eed76d 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts @@ -6,7 +6,7 @@ */ import { camelCase } from 'lodash'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { HttpStart } from 'src/core/public'; import { diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_preview_rule.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_preview_rule.ts index b610e96273ebd..3d813c8d10a68 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_preview_rule.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_preview_rule.ts @@ -7,7 +7,7 @@ import { useEffect, useState } from 'react'; -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import { RULE_PREVIEW_FROM, RULE_PREVIEW_INTERVAL, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts index e18886166f470..49f8b386108b1 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts @@ -6,7 +6,7 @@ */ import { has, isEmpty } from 'lodash/fp'; -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import moment from 'moment'; import deepmerge from 'deepmerge'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx index 39a6b12d63132..d91eed9301ae4 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import moment from 'moment'; import memoizeOne from 'memoize-one'; import { useLocation } from 'react-router-dom'; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/utils.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/utils.ts index 1bfb99c68ef66..96687895b99c6 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/utils.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/utils.ts @@ -5,7 +5,7 @@ * 2.0. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import moment from 'moment'; import { HostInfo, HostMetadata } from '../../../../common/endpoint/types'; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/activity_log_date_range_picker/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/activity_log_date_range_picker/index.tsx index a57fa8d8e4ce5..423f6188c1b8c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/activity_log_date_range_picker/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/activity_log_date_range_picker/index.tsx @@ -8,7 +8,7 @@ import { useDispatch } from 'react-redux'; import React, { memo, useCallback } from 'react'; import styled from 'styled-components'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { EuiFlexGroup, EuiFlexItem, diff --git a/x-pack/plugins/security_solution/public/transforms/utils/adjust_timerange.ts b/x-pack/plugins/security_solution/public/transforms/utils/adjust_timerange.ts index e9334379d7b0b..464ccd8692512 100644 --- a/x-pack/plugins/security_solution/public/transforms/utils/adjust_timerange.ts +++ b/x-pack/plugins/security_solution/public/transforms/utils/adjust_timerange.ts @@ -5,7 +5,7 @@ * 2.0. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import moment, { Duration } from 'moment'; import type { TimerangeInput } from '../../../common/search_strategy'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts index e7810e6fe0078..288fd7a688e3c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { loggingSystemMock } from 'src/core/server/mocks'; import { alertsMock, RuleExecutorServicesMock } from '../../../../../../alerting/server/mocks'; import { eqlExecutor } from './eql'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.test.ts index 417382b0bd05a..bc756bf9681e3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { loggingSystemMock } from 'src/core/server/mocks'; import { alertsMock, RuleExecutorServicesMock } from '../../../../../../alerting/server/mocks'; import { mlExecutor } from './ml'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts index 14c56aa3bc9be..01e61b6917cc4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { loggingSystemMock } from 'src/core/server/mocks'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mocks'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_ml_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_ml_signals.ts index 10f89b56229dc..7d2041d86e395 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_ml_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_ml_signals.ts @@ -5,7 +5,7 @@ * 2.0. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { KibanaRequest, SavedObjectsClientContract } from '../../../../../../../src/core/server'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts index a0d73707466ce..738a9d9dd0c50 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts @@ -9,7 +9,7 @@ import { chunk, get, invert, isEmpty, partition } from 'lodash'; import moment from 'moment'; import uuidv5 from 'uuid/v5'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { TransportResult } from '@elastic/elasticsearch'; import { ALERT_UUID, ALERT_RULE_UUID, ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils'; diff --git a/x-pack/plugins/triggers_actions_ui/common/parse_interval.ts b/x-pack/plugins/triggers_actions_ui/common/parse_interval.ts index 21fd1b214c32f..39d425bbb9ddd 100644 --- a/x-pack/plugins/triggers_actions_ui/common/parse_interval.ts +++ b/x-pack/plugins/triggers_actions_ui/common/parse_interval.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { i18n } from '@kbn/i18n'; export const INTERVAL_STRING_RE = new RegExp(`^([\\d\\.]+)\\s*(${dateMath.units.join('|')})$`); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_error_log.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_error_log.tsx index 6eb6c732665e3..1c4dd438e180d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_error_log.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_error_log.tsx @@ -7,7 +7,7 @@ import React, { useCallback, useEffect, useState, useMemo, useRef } from 'react'; import { i18n } from '@kbn/i18n'; -import datemath from '@elastic/datemath'; +import datemath from '@kbn/datemath'; import { EuiFlexItem, EuiFlexGroup, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list.tsx index 852ad9e7c8024..7cc198c1bf58e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list.tsx @@ -7,7 +7,7 @@ import React, { useCallback, useEffect, useState, useMemo, useRef } from 'react'; import { i18n } from '@kbn/i18n'; -import datemath from '@elastic/datemath'; +import datemath from '@kbn/datemath'; import { EuiDataGrid, EuiFlexItem, diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/handlebars.ts b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/handlebars.ts index 3f831bc5c9057..fc4ce4c3d9625 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/handlebars.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/handlebars.ts @@ -7,7 +7,7 @@ import { create as createHandlebars, HelperDelegate, HelperOptions } from 'handlebars'; import { encode, RisonValue } from 'rison-node'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import moment, { Moment } from 'moment'; import numeral from '@elastic/numeral'; import { url } from '../../../../../../src/plugins/kibana_utils/public'; diff --git a/x-pack/plugins/uptime/common/lib/get_histogram_interval.ts b/x-pack/plugins/uptime/common/lib/get_histogram_interval.ts index 58b04bb041580..8d44fb594c03a 100644 --- a/x-pack/plugins/uptime/common/lib/get_histogram_interval.ts +++ b/x-pack/plugins/uptime/common/lib/get_histogram_interval.ts @@ -5,7 +5,7 @@ * 2.0. */ -import DateMath from '@elastic/datemath'; +import DateMath from '@kbn/datemath'; import { QUERY } from '../constants'; export const parseRelativeDate = (dateStr: string, options = {}) => { diff --git a/x-pack/plugins/uptime/public/components/common/charts/duration_charts.test.tsx b/x-pack/plugins/uptime/public/components/common/charts/duration_charts.test.tsx index 7669ee29786f3..fee4143df11f9 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/duration_charts.test.tsx +++ b/x-pack/plugins/uptime/public/components/common/charts/duration_charts.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import DateMath from '@elastic/datemath'; +import DateMath from '@kbn/datemath'; import { DurationChartComponent } from './duration_chart'; import { MonitorDurationResult } from '../../../../common/types'; import { render } from '../../../lib/helper/rtl_helpers'; diff --git a/x-pack/plugins/uptime/public/components/common/charts/ping_histogram.test.tsx b/x-pack/plugins/uptime/public/components/common/charts/ping_histogram.test.tsx index 602f1b2898a96..345d23ff22b45 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/ping_histogram.test.tsx +++ b/x-pack/plugins/uptime/public/components/common/charts/ping_histogram.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import DateMath from '@elastic/datemath'; +import DateMath from '@kbn/datemath'; import { PingHistogramComponent, PingHistogramComponentProps } from './ping_histogram'; import { render } from '../../../lib/helper/rtl_helpers'; import { mockDataPlugin, mockMoment, mockMomentTimezone } from '../../../lib/helper/test_helpers'; diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_charts.test.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_charts.test.tsx index 3f107581c1eea..5e3007ed36a17 100644 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_charts.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/monitor_charts.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import DateMath from '@elastic/datemath'; +import DateMath from '@kbn/datemath'; import { MonitorCharts } from './monitor_charts'; import { shallowWithRouter } from '../../lib'; diff --git a/x-pack/plugins/uptime/public/hooks/use_url_params.test.tsx b/x-pack/plugins/uptime/public/hooks/use_url_params.test.tsx index c62f254afccb3..619946d21a263 100644 --- a/x-pack/plugins/uptime/public/hooks/use_url_params.test.tsx +++ b/x-pack/plugins/uptime/public/hooks/use_url_params.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import DateMath from '@elastic/datemath'; +import DateMath from '@kbn/datemath'; import React, { useState, Fragment } from 'react'; import { useUrlParams, UptimeUrlParamsHook } from './use_url_params'; import { UptimeRefreshContext } from '../contexts'; diff --git a/x-pack/plugins/uptime/public/lib/helper/url_params/get_supported_url_params.test.ts b/x-pack/plugins/uptime/public/lib/helper/url_params/get_supported_url_params.test.ts index e0ee5574b48d0..4771d864e0bf8 100644 --- a/x-pack/plugins/uptime/public/lib/helper/url_params/get_supported_url_params.test.ts +++ b/x-pack/plugins/uptime/public/lib/helper/url_params/get_supported_url_params.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import DateMath from '@elastic/datemath'; +import DateMath from '@kbn/datemath'; import { getSupportedUrlParams } from './get_supported_url_params'; import { CLIENT_DEFAULTS } from '../../../../common/constants'; diff --git a/x-pack/plugins/uptime/public/lib/helper/url_params/parse_absolute_date.test.ts b/x-pack/plugins/uptime/public/lib/helper/url_params/parse_absolute_date.test.ts index 8f597bcd0912f..c9a5a3081b17a 100644 --- a/x-pack/plugins/uptime/public/lib/helper/url_params/parse_absolute_date.test.ts +++ b/x-pack/plugins/uptime/public/lib/helper/url_params/parse_absolute_date.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import DateMath from '@elastic/datemath'; +import DateMath from '@kbn/datemath'; import moment from 'moment'; import { parseAbsoluteDate } from './parse_absolute_date'; diff --git a/x-pack/plugins/uptime/public/lib/helper/url_params/parse_absolute_date.ts b/x-pack/plugins/uptime/public/lib/helper/url_params/parse_absolute_date.ts index 5a3627c048860..54b555471451c 100644 --- a/x-pack/plugins/uptime/public/lib/helper/url_params/parse_absolute_date.ts +++ b/x-pack/plugins/uptime/public/lib/helper/url_params/parse_absolute_date.ts @@ -5,7 +5,7 @@ * 2.0. */ -import DateMath from '@elastic/datemath'; +import DateMath from '@kbn/datemath'; export const parseAbsoluteDate = (date: string, defaultValue: number, options = {}): number => { const momentWrapper = DateMath.parse(date, options); diff --git a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts index 6d9a0d23d9d32..007ad8672eb2f 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts @@ -6,7 +6,7 @@ */ import { min } from 'lodash'; -import datemath from '@elastic/datemath'; +import datemath from '@kbn/datemath'; import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; import { JsonObject } from '@kbn/utility-types'; diff --git a/x-pack/plugins/ux/public/context/url_params_context/helpers.test.ts b/x-pack/plugins/ux/public/context/url_params_context/helpers.test.ts index 784b10b3f3ee1..b76b53551db08 100644 --- a/x-pack/plugins/ux/public/context/url_params_context/helpers.test.ts +++ b/x-pack/plugins/ux/public/context/url_params_context/helpers.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import datemath from '@elastic/datemath'; +import datemath from '@kbn/datemath'; import moment from 'moment-timezone'; import * as helpers from './helpers'; diff --git a/x-pack/plugins/ux/public/context/url_params_context/helpers.ts b/x-pack/plugins/ux/public/context/url_params_context/helpers.ts index ee6ac43c1aeab..728b89eb2bad7 100644 --- a/x-pack/plugins/ux/public/context/url_params_context/helpers.ts +++ b/x-pack/plugins/ux/public/context/url_params_context/helpers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import datemath from '@elastic/datemath'; +import datemath from '@kbn/datemath'; import { compact, pickBy } from 'lodash'; import moment from 'moment'; import { UrlParams } from './types'; diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx index fc6ad7ae4a7d8..23028075daa3b 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx @@ -16,7 +16,7 @@ import { ScaleType, Settings, } from '@elastic/charts'; -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import moment from 'moment-timezone'; import { IUiSettingsClient } from 'kibana/public'; import { EuiCallOut, EuiLoadingChart, EuiSpacer, EuiEmptyPrompt, EuiText } from '@elastic/eui'; diff --git a/x-pack/plugins/watcher/public/legacy/calc_es_interval.ts b/x-pack/plugins/watcher/public/legacy/calc_es_interval.ts index cae88b797ea4f..d60f8955bcbd6 100644 --- a/x-pack/plugins/watcher/public/legacy/calc_es_interval.ts +++ b/x-pack/plugins/watcher/public/legacy/calc_es_interval.ts @@ -5,7 +5,7 @@ * 2.0. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import { parseEsInterval } from './parse_es_interval'; diff --git a/x-pack/plugins/watcher/public/legacy/parse_es_interval/invalid_es_calendar_interval_error.ts b/x-pack/plugins/watcher/public/legacy/parse_es_interval/invalid_es_calendar_interval_error.ts index 6ff68a1254808..6c9ca8b4a95fe 100644 --- a/x-pack/plugins/watcher/public/legacy/parse_es_interval/invalid_es_calendar_interval_error.ts +++ b/x-pack/plugins/watcher/public/legacy/parse_es_interval/invalid_es_calendar_interval_error.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Unit } from '@elastic/datemath'; +import { Unit } from '@kbn/datemath'; import { i18n } from '@kbn/i18n'; export class InvalidEsCalendarIntervalError extends Error { diff --git a/x-pack/plugins/watcher/public/legacy/parse_es_interval/parse_es_interval.ts b/x-pack/plugins/watcher/public/legacy/parse_es_interval/parse_es_interval.ts index 968ae01587523..a0cf6af6dd331 100644 --- a/x-pack/plugins/watcher/public/legacy/parse_es_interval/parse_es_interval.ts +++ b/x-pack/plugins/watcher/public/legacy/parse_es_interval/parse_es_interval.ts @@ -5,7 +5,7 @@ * 2.0. */ -import dateMath, { Unit } from '@elastic/datemath'; +import dateMath, { Unit } from '@kbn/datemath'; import { InvalidEsCalendarIntervalError } from './invalid_es_calendar_interval_error'; import { InvalidEsIntervalFormatError } from './invalid_es_interval_format_error'; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/get_rule_execution_events.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/get_rule_execution_events.ts index 555c16e206ab8..b8287c4433ba4 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/get_rule_execution_events.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/get_rule_execution_events.ts @@ -5,7 +5,7 @@ * 2.0. */ -import dateMath from '@elastic/datemath'; +import dateMath from '@kbn/datemath'; import expect from '@kbn/expect'; import moment from 'moment'; import { set } from '@elastic/safer-lodash-set'; diff --git a/x-pack/test/functional/apps/rollup_job/hybrid_index_pattern.js b/x-pack/test/functional/apps/rollup_job/hybrid_index_pattern.js index 336a575454e72..04a6a848d3cbd 100644 --- a/x-pack/test/functional/apps/rollup_job/hybrid_index_pattern.js +++ b/x-pack/test/functional/apps/rollup_job/hybrid_index_pattern.js @@ -5,7 +5,7 @@ * 2.0. */ -import datemath from '@elastic/datemath'; +import datemath from '@kbn/datemath'; import expect from '@kbn/expect'; import mockRolledUpData, { mockIndices } from './hybrid_index_helper'; diff --git a/x-pack/test/functional/apps/rollup_job/rollup_jobs.js b/x-pack/test/functional/apps/rollup_job/rollup_jobs.js index 4f535ae3e3ef0..e88082e9f00a8 100644 --- a/x-pack/test/functional/apps/rollup_job/rollup_jobs.js +++ b/x-pack/test/functional/apps/rollup_job/rollup_jobs.js @@ -5,7 +5,7 @@ * 2.0. */ -import datemath from '@elastic/datemath'; +import datemath from '@kbn/datemath'; import expect from '@kbn/expect'; import { mockIndices } from './hybrid_index_helper'; diff --git a/yarn.lock b/yarn.lock index ea4324cd13607..bf1639a09b849 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1228,15 +1228,23 @@ resolved "https://registry.yarnpkg.com/@bazel/ibazel/-/ibazel-0.16.2.tgz#05dd7f06659759fda30f87b15534f1e42f1201bb" integrity sha512-KgqAWMH0emL6f3xH6nqyTryoBMqlJ627LBIe9PT1PRRQPz2FtHib3FIHJPukp1slzF3hJYZvdiVwgPnHbaSOOA== -"@bazel/typescript@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-4.0.0.tgz#71a6f0f5e340c6b8516b21fbc0f0853e74055d0c" - integrity sha512-+Le9q+5IR9gEnSH8sXyxDB5dD6NJx2kbm6AL+cijYVat2MczpGV4sI1mu0mdLzYsEX5Tjt5iHkaNb7sFESjnYA== +"@bazel/typescript@5.3.1": + version "5.3.1" + resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-5.3.1.tgz#f996abebfa57a29a170a83d0c84780286f06cb72" + integrity sha512-fP0fGzLbsOVdbAn6mlrX1LBAT0TJ+S0VVOap9SSSXCb2PknnKEHZupUww/raVSczy+cqcd/Vfpllzm/AcdOmyw== dependencies: + "@bazel/worker" "5.3.1" protobufjs "6.8.8" semver "5.6.0" source-map-support "0.5.9" - tsutils "2.27.2" + tsutils "3.21.0" + +"@bazel/worker@5.3.1": + version "5.3.1" + resolved "https://registry.yarnpkg.com/@bazel/worker/-/worker-5.3.1.tgz#87d850fdfc22cde24f380961f66daa5cbfd4acf7" + integrity sha512-KB+Xs198NLhN01zhlOPr/VEmz/tzwGq/pI1gdnCTLDMTCspV6LnJaIeGgicXuKrzz0V1LhvlvsI5lqJt9yuGYw== + dependencies: + google-protobuf "^3.6.1" "@bcoe/v8-coverage@^0.2.3": version "0.2.3" @@ -1471,9 +1479,12 @@ utility-types "^3.10.0" uuid "^3.3.2" -"@elastic/datemath@link:bazel-bin/packages/elastic-datemath": - version "0.0.0" - uid "" +"@elastic/datemath@5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@elastic/datemath/-/datemath-5.0.3.tgz#7baccdab672b9a3ecb7fe8387580670936b58573" + integrity sha512-8Hbr1Uyjm5OcYBfEB60K7sCP6U3IXuWDaLaQmYv3UxgI4jqBWbakoemwWvsqPVUvnwEjuX6z7ghPZbefs8xiaA== + dependencies: + tslib "^1.9.3" "@elastic/ecs-helpers@^1.1.0": version "1.1.0" @@ -2980,6 +2991,10 @@ version "0.0.0" uid "" +"@kbn/datemath@link:bazel-bin/packages/kbn-datemath": + version "0.0.0" + uid "" + "@kbn/dev-utils@link:bazel-bin/packages/kbn-dev-utils": version "0.0.0" uid "" @@ -5557,10 +5572,6 @@ version "0.0.0" uid "" -"@types/elastic__datemath@link:bazel-bin/packages/elastic-datemath/npm_module_types": - version "0.0.0" - uid "" - "@types/enzyme@^3.10.8": version "3.10.8" resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.10.8.tgz#ad7ac9d3af3de6fd0673773123fafbc63db50d42" @@ -6007,6 +6018,10 @@ version "0.0.0" uid "" +"@types/kbn__datemath@link:bazel-bin/packages/kbn-datemath/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__dev-utils@link:bazel-bin/packages/kbn-dev-utils/npm_module_types": version "0.0.0" uid "" @@ -15447,6 +15462,11 @@ gonzales-pe@^4.3.0: dependencies: minimist "^1.2.5" +google-protobuf@^3.6.1: + version "3.19.4" + resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.19.4.tgz#8d32c3e34be9250956f28c0fb90955d13f311888" + integrity sha512-OIPNCxsG2lkIvf+P5FNfJ/Km95CsXOBecS9ZcAU6m2Rq3svc0Apl9nB3GMDNKfQ9asNv4KjyAqGwPQFrVle3Yg== + got@11.8.2, got@^11.8.2: version "11.8.2" resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599" @@ -28149,14 +28169,7 @@ tslib@~2.1.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== -tsutils@2.27.2: - version "2.27.2" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.27.2.tgz#60ba88a23d6f785ec4b89c6e8179cac9b431f1c7" - integrity sha512-qf6rmT84TFMuxAKez2pIfR8UCai49iQsfB7YWVjV1bKpy/d0PWT5rEOSM6La9PiHZ0k1RRZQiwVdVJfQ3BPHgg== - dependencies: - tslib "^1.8.1" - -tsutils@^3.21.0: +tsutils@3.21.0, tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== From 7b64a5435dc3392c31ae2865bc2ff9cc729f9117 Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Fri, 8 Apr 2022 17:42:39 -0400 Subject: [PATCH 19/42] [Maps] Apply styles to icon SVGs in legend via descendent selectors (#129255) --- .../__snapshots__/symbol_utils.test.js.snap | 63 +++++++++++++++++++ .../vector/components/legend/symbol_icon.tsx | 2 +- .../classes/styles/vector/symbol_utils.js | 20 +++--- .../styles/vector/symbol_utils.test.js | 25 +++++--- .../map_settings_panel/map_settings_panel.tsx | 12 ++-- 5 files changed, 97 insertions(+), 25 deletions(-) create mode 100644 x-pack/plugins/maps/public/classes/styles/vector/__snapshots__/symbol_utils.test.js.snap diff --git a/x-pack/plugins/maps/public/classes/styles/vector/__snapshots__/symbol_utils.test.js.snap b/x-pack/plugins/maps/public/classes/styles/vector/__snapshots__/symbol_utils.test.js.snap new file mode 100644 index 0000000000000..d4936639523c3 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/styles/vector/__snapshots__/symbol_utils.test.js.snap @@ -0,0 +1,63 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`styleSvg Should add fill style property to svg element 1`] = ` +" + + + +" +`; + +exports[`styleSvg Should add stroke and stroke-wdth style properties to svg element 1`] = ` +" + + + +" +`; + +exports[`styleSvg Should not add style property when style not provided 1`] = ` +" + + + +" +`; + +exports[`styleSvg Should override any inherent fill and stroke styles in SVGs 1`] = ` +" + + + + + +" +`; diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/legend/symbol_icon.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/symbol_icon.tsx index 4cc4d4169d7e0..fd9b952dbbdea 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/legend/symbol_icon.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/symbol_icon.tsx @@ -7,7 +7,7 @@ import React, { Component, CSSProperties } from 'react'; // @ts-expect-error -import { CUSTOM_ICON_PREFIX_SDF, getSymbolSvg, styleSvg, buildSrcUrl } from '../../symbol_utils'; +import { styleSvg, buildSrcUrl } from '../../symbol_utils'; interface Props { symbolId: string; diff --git a/x-pack/plugins/maps/public/classes/styles/vector/symbol_utils.js b/x-pack/plugins/maps/public/classes/styles/vector/symbol_utils.js index af165863ffc9c..108a2eb686dcf 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/symbol_utils.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/symbol_utils.js @@ -106,15 +106,17 @@ export function buildSrcUrl(svgString) { export async function styleSvg(svgString, fill, stroke) { const svgXml = await parseXmlString(svgString); - let style = ''; - if (fill) { - style += `fill:${fill};`; - } - if (stroke) { - style += `stroke:${stroke};`; - style += `stroke-width:1;`; - } - if (style) svgXml.svg.$.style = style; + + // Elements nested under svg root may define style attribute + // Wildcard descendent selector provides more specificity to ensure root svg style attribute is applied instead of children style attributes + svgXml.svg.style = ` + svg * { + ${fill ? `fill: ${fill}` : '#000'} !important; + ${stroke ? `stroke: ${stroke}` : '#000'} !important; + stroke-width: 1 !important; + vector-effect: non-scaling-stroke !important; + } + `; const builder = new xml2js.Builder(); return builder.buildObject(svgXml); } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/symbol_utils.test.js b/x-pack/plugins/maps/public/classes/styles/vector/symbol_utils.test.js index 8c85702b19579..f6d40bd70dbea 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/symbol_utils.test.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/symbol_utils.test.js @@ -20,26 +20,33 @@ describe('styleSvg', () => { const unstyledSvgString = ''; const styledSvg = await styleSvg(unstyledSvgString); - expect(styledSvg.split('\n')[1]).toBe( - '' - ); + expect(styledSvg).toMatchSnapshot(); }); it('Should add fill style property to svg element', async () => { const unstyledSvgString = ''; const styledSvg = await styleSvg(unstyledSvgString, 'red'); - expect(styledSvg.split('\n')[1]).toBe( - '' - ); + expect(styledSvg).toMatchSnapshot(); }); it('Should add stroke and stroke-wdth style properties to svg element', async () => { const unstyledSvgString = ''; const styledSvg = await styleSvg(unstyledSvgString, 'red', 'white'); - expect(styledSvg.split('\n')[1]).toBe( - '' - ); + expect(styledSvg).toMatchSnapshot(); + }); + + it('Should override any inherent fill and stroke styles in SVGs', async () => { + const unstyledSvgString = ` + + + + + + `; + + const styledSvg = await styleSvg(unstyledSvgString, 'blue', 'black'); + expect(styledSvg).toMatchSnapshot(); }); }); diff --git a/x-pack/plugins/maps/public/connected_components/map_settings_panel/map_settings_panel.tsx b/x-pack/plugins/maps/public/connected_components/map_settings_panel/map_settings_panel.tsx index 1efa07e280039..005c40f48cce6 100644 --- a/x-pack/plugins/maps/public/connected_components/map_settings_panel/map_settings_panel.tsx +++ b/x-pack/plugins/maps/public/connected_components/map_settings_panel/map_settings_panel.tsx @@ -74,6 +74,12 @@ export function MapSettingsPanel({
+ + - -
From 27ff7d342416bda8b75600f45a07a6d82d3e7560 Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Sat, 9 Apr 2022 10:04:14 -0400 Subject: [PATCH 20/42] Test config settings that are exposed to the browser (#129438) --- .github/CODEOWNERS | 1 + packages/kbn-config-schema/src/index.ts | 3 +- packages/kbn-config-schema/src/types/index.ts | 1 + .../src/types/object_type.test.ts | 73 ++++++ .../src/types/string_type.ts | 1 + packages/kbn-config-schema/src/types/type.ts | 23 ++ .../http_resources/http_resources_service.ts | 2 + src/core/server/http_resources/types.ts | 5 + .../plugins/create_browser_config.test.ts | 231 ++++++++++-------- .../server/plugins/create_browser_config.ts | 52 +++- .../server/plugins/plugins_service.test.ts | 6 +- src/core/server/plugins/plugins_service.ts | 4 +- .../server/rendering/rendering_service.tsx | 21 +- src/core/server/rendering/types.ts | 6 + src/core/server/server.api.md | 4 + .../plugins/rendering_plugin/server/plugin.ts | 4 +- .../test_suites/core_plugins/rendering.ts | 211 +++++++++++++++- 17 files changed, 521 insertions(+), 127 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9c1599a60dd0c..3c991e6a61d53 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -324,6 +324,7 @@ /src/plugins/interactive_setup/ @elastic/kibana-security /test/interactive_setup_api_integration/ @elastic/kibana-security /test/interactive_setup_functional/ @elastic/kibana-security +/test/plugin_functional/test_suites/core_plugins/rendering.ts @elastic/kibana-security /x-pack/plugins/spaces/ @elastic/kibana-security /x-pack/plugins/encrypted_saved_objects/ @elastic/kibana-security /x-pack/plugins/security/ @elastic/kibana-security diff --git a/packages/kbn-config-schema/src/index.ts b/packages/kbn-config-schema/src/index.ts index f9db84f255ec6..af545ac5eb9e6 100644 --- a/packages/kbn-config-schema/src/index.ts +++ b/packages/kbn-config-schema/src/index.ts @@ -38,6 +38,7 @@ import { NullableProps, RecordOfOptions, RecordOfType, + SchemaStructureEntry, StringOptions, StringType, Type, @@ -49,7 +50,7 @@ import { StreamType, } from './types'; -export type { AnyType, ConditionalType, TypeOf, Props, NullableProps }; +export type { AnyType, ConditionalType, TypeOf, Props, SchemaStructureEntry, NullableProps }; export { ObjectType, Type }; export { ByteSizeValue } from './byte_size_value'; export { SchemaTypeError, ValidationError } from './errors'; diff --git a/packages/kbn-config-schema/src/types/index.ts b/packages/kbn-config-schema/src/types/index.ts index 5152137985ff3..23b114fd824df 100644 --- a/packages/kbn-config-schema/src/types/index.ts +++ b/packages/kbn-config-schema/src/types/index.ts @@ -7,6 +7,7 @@ */ export type { TypeOptions } from './type'; +export type { SchemaStructureEntry } from './type'; export { Type } from './type'; export { AnyType } from './any_type'; export type { ArrayOptions } from './array_type'; diff --git a/packages/kbn-config-schema/src/types/object_type.test.ts b/packages/kbn-config-schema/src/types/object_type.test.ts index 67f0963fefdda..6548e808395b9 100644 --- a/packages/kbn-config-schema/src/types/object_type.test.ts +++ b/packages/kbn-config-schema/src/types/object_type.test.ts @@ -490,3 +490,76 @@ describe('#extends', () => { expect(extended.validate(undefined)).toEqual({ initial: 'bar', added: 42 }); }); }); + +test('returns schema structure', () => { + // This test covers different schema types that may or may not be nested + const objSchema = schema.object({ + any: schema.any(), + array: schema.arrayOf(schema.string()), + boolean: schema.boolean(), + buffer: schema.buffer(), + byteSize: schema.byteSize(), + conditional: schema.conditional( + schema.contextRef('context_value_1'), + schema.contextRef('context_value_2'), + schema.string(), + schema.string() + ), + duration: schema.duration(), + ip: schema.ip(), + literal: schema.literal('foo'), + map: schema.mapOf(schema.string(), schema.string()), + maybe: schema.maybe(schema.string()), + never: schema.never(), + nullable: schema.nullable(schema.string()), + number: schema.number(), + record: schema.recordOf(schema.string(), schema.string()), + stream: schema.stream(), + string: schema.string(), + union: schema.oneOf([schema.string()]), + uri: schema.uri(), + }); + const type = objSchema.extends({ + nested: objSchema, + }); + expect(type.getSchemaStructure()).toEqual([ + { path: ['any'], type: 'any' }, + { path: ['array'], type: 'array' }, + { path: ['boolean'], type: 'boolean' }, + { path: ['buffer'], type: 'binary' }, + { path: ['byteSize'], type: 'bytes' }, + { path: ['conditional'], type: 'any' }, + { path: ['duration'], type: 'duration' }, + { path: ['ip'], type: 'string' }, + { path: ['literal'], type: 'any' }, + { path: ['map'], type: 'map' }, + { path: ['maybe'], type: 'string' }, + { path: ['never'], type: 'any' }, + { path: ['nullable'], type: 'alternatives' }, + { path: ['number'], type: 'number' }, + { path: ['record'], type: 'record' }, + { path: ['stream'], type: 'stream' }, + { path: ['string'], type: 'string' }, + { path: ['union'], type: 'alternatives' }, + { path: ['uri'], type: 'string' }, + { path: ['nested', 'any'], type: 'any' }, + { path: ['nested', 'array'], type: 'array' }, + { path: ['nested', 'boolean'], type: 'boolean' }, + { path: ['nested', 'buffer'], type: 'binary' }, + { path: ['nested', 'byteSize'], type: 'bytes' }, + { path: ['nested', 'conditional'], type: 'any' }, + { path: ['nested', 'duration'], type: 'duration' }, + { path: ['nested', 'ip'], type: 'string' }, + { path: ['nested', 'literal'], type: 'any' }, + { path: ['nested', 'map'], type: 'map' }, + { path: ['nested', 'maybe'], type: 'string' }, + { path: ['nested', 'never'], type: 'any' }, + { path: ['nested', 'nullable'], type: 'alternatives' }, + { path: ['nested', 'number'], type: 'number' }, + { path: ['nested', 'record'], type: 'record' }, + { path: ['nested', 'stream'], type: 'stream' }, + { path: ['nested', 'string'], type: 'string' }, + { path: ['nested', 'union'], type: 'alternatives' }, + { path: ['nested', 'uri'], type: 'string' }, + ]); +}); diff --git a/packages/kbn-config-schema/src/types/string_type.ts b/packages/kbn-config-schema/src/types/string_type.ts index 1442c5b9b4efb..f2792e4031e0a 100644 --- a/packages/kbn-config-schema/src/types/string_type.ts +++ b/packages/kbn-config-schema/src/types/string_type.ts @@ -53,6 +53,7 @@ export class StringType extends Type { ); } + schema.type = 'string'; super(schema, options); } diff --git a/packages/kbn-config-schema/src/types/type.ts b/packages/kbn-config-schema/src/types/type.ts index 696101fb2c223..d3bab0106e5c4 100644 --- a/packages/kbn-config-schema/src/types/type.ts +++ b/packages/kbn-config-schema/src/types/type.ts @@ -15,6 +15,11 @@ export interface TypeOptions { validate?: (value: T) => string | void; } +export interface SchemaStructureEntry { + path: string[]; + type: string; +} + export const convertValidationFunction = ( validate: (value: T) => string | void ): CustomValidator => { @@ -98,6 +103,10 @@ export abstract class Type { return this.internalSchema; } + public getSchemaStructure() { + return recursiveGetSchemaStructure(this.internalSchema); + } + protected handleError( type: string, context: Record, @@ -141,3 +150,17 @@ export abstract class Type { return new SchemaTypeError(message || code, convertedPath); } } + +function recursiveGetSchemaStructure(internalSchema: AnySchema, path: string[] = []) { + const array: SchemaStructureEntry[] = []; + // Note: we are relying on Joi internals to obtain the schema structure (recursive keys). + // This is not ideal, but it works for now and we only need it for some integration test assertions. + // If it breaks in the future, we'll need to update our tests. + for (const [key, val] of (internalSchema as any)._ids._byKey.entries()) { + array.push(...recursiveGetSchemaStructure(val.schema, [...path, key])); + } + if (!array.length) { + array.push({ path, type: internalSchema.type ?? 'unknown' }); + } + return array; +} diff --git a/src/core/server/http_resources/http_resources_service.ts b/src/core/server/http_resources/http_resources_service.ts index 25467152ce4fe..95610d36d4230 100644 --- a/src/core/server/http_resources/http_resources_service.ts +++ b/src/core/server/http_resources/http_resources_service.ts @@ -98,6 +98,7 @@ export class HttpResourcesService implements CoreService { it('picks nothing by default', () => { + const configSchema = schema.object({ + notExposed1: schema.string(), + nested: schema.object({ + notExposed2: schema.boolean(), + notExposed3: schema.maybe(schema.number()), + }), + }); const config = { - foo: 'bar', + notExposed1: '1', nested: { - str: 'string', - num: 42, + notExposed2: true, + notExposed3: 3, }, }; - const descriptor: ExposedToBrowserDescriptor = {}; - - const browserConfig = createBrowserConfig(config, descriptor); + const descriptor: PluginConfigDescriptor> = { + schema: configSchema, + }; - expect(browserConfig).toEqual({}); + const result = createBrowserConfig(config, descriptor); + expect(result).toEqual({ browserConfig: {}, exposedConfigKeys: {} }); }); it('picks all the nested properties when using `true`', () => { - const config = { - foo: 'bar', - nested: { - str: 'string', - num: 42, - }, - }; - - const descriptor: ExposedToBrowserDescriptor = { - foo: true, - nested: true, - }; - - const browserConfig = createBrowserConfig(config, descriptor); - - expect(browserConfig).toEqual({ - foo: 'bar', - nested: { - str: 'string', - num: 42, - }, + const configSchema = schema.object({ + exposed1: schema.string(), + nested: schema.object({ + exposed2: schema.boolean(), + exposed3: schema.maybe(schema.number()), + }), + notExposed4: schema.string(), }); - }); - - it('picks specific nested properties when using a nested declaration', () => { const config = { - foo: 'bar', + exposed1: '1', nested: { - str: 'string', - num: 42, + exposed2: true, + exposed3: 3, }, + notExposed4: '4', }; - - const descriptor: ExposedToBrowserDescriptor = { - foo: true, - nested: { - str: true, - num: false, + const descriptor: PluginConfigDescriptor> = { + schema: configSchema, + exposeToBrowser: { + exposed1: true, + nested: true, }, }; - const browserConfig = createBrowserConfig(config, descriptor); - - expect(browserConfig).toEqual({ - foo: 'bar', - nested: { - str: 'string', + const result = createBrowserConfig(config, descriptor); + expect(result).toEqual({ + browserConfig: { + exposed1: '1', + nested: { exposed2: true, exposed3: 3 }, + // notExposed4 is not present + }, + exposedConfigKeys: { + exposed1: 'string', + 'nested.exposed2': 'boolean', + 'nested.exposed3': 'number', + // notExposed4 is not present }, }); }); - it('accepts deeply nested structures', () => { + it('picks specific nested properties, omitting those which are not specified', () => { + const configSchema = schema.object({ + exposed1: schema.string(), + nested: schema.object({ + exposed2: schema.boolean(), + notExposed3: schema.maybe(schema.number()), + }), + notExposed4: schema.string(), + }); const config = { - foo: 'bar', - deeply: { - str: 'string', - nested: { - hello: 'dolly', - structure: { - propA: 'propA', - propB: 'propB', - }, - }, + exposed1: '1', + nested: { + exposed2: true, + notExposed3: 3, }, + notExposed4: '4', }; - - const descriptor: ExposedToBrowserDescriptor = { - foo: false, - deeply: { - str: false, - nested: { - hello: true, - structure: { - propA: true, - propB: false, - }, - }, + const descriptor: PluginConfigDescriptor> = { + schema: configSchema, + exposeToBrowser: { + exposed1: true, + nested: { exposed2: true }, }, }; - const browserConfig = createBrowserConfig(config, descriptor); - - expect(browserConfig).toEqual({ - deeply: { - nested: { - hello: 'dolly', - structure: { - propA: 'propA', - }, - }, + const result = createBrowserConfig(config, descriptor); + expect(result).toEqual({ + browserConfig: { + exposed1: '1', + nested: { exposed2: true }, + // notExposed3 and notExposed4 are not present + }, + exposedConfigKeys: { + exposed1: 'string', + 'nested.exposed2': 'boolean', + // notExposed3 and notExposed4 are not present }, }); }); - it('only includes leaf properties that are `true` when in nested structures', () => { + it('picks specific deeply nested properties, omitting those which are not specified', () => { + const configSchema = schema.object({ + exposed1: schema.string(), + deeply: schema.object({ + exposed2: schema.boolean(), + nested: schema.object({ + exposed3: schema.maybe(schema.number()), + structure: schema.object({ + exposed4: schema.string(), + notExposed5: schema.string(), + }), + notExposed6: schema.string(), + }), + notExposed7: schema.string(), + }), + notExposed8: schema.string(), + }); const config = { - foo: 'bar', + exposed1: '1', deeply: { - str: 'string', + exposed2: true, nested: { - hello: 'dolly', + exposed3: 3, structure: { - propA: 'propA', - propB: 'propB', + exposed4: '4', + notExposed5: '5', }, + notExposed6: '6', }, + notExposed7: '7', }, + notExposed8: '8', }; - - const descriptor: ExposedToBrowserDescriptor = { - deeply: { - nested: { - hello: true, - structure: { - propA: true, + const descriptor: PluginConfigDescriptor> = { + schema: configSchema, + exposeToBrowser: { + exposed1: true, + deeply: { + exposed2: true, + nested: { + exposed3: true, + structure: { + exposed4: true, + }, }, }, }, }; - const browserConfig = createBrowserConfig(config, descriptor); - - expect(browserConfig).toEqual({ - deeply: { - nested: { - hello: 'dolly', - structure: { - propA: 'propA', + const result = createBrowserConfig(config, descriptor); + expect(result).toEqual({ + browserConfig: { + exposed1: '1', + deeply: { + exposed2: true, + nested: { + exposed3: 3, + structure: { + exposed4: '4', + }, }, }, + // notExposed5, notExposed6, notExposed7, and notExposed8 are not present + }, + exposedConfigKeys: { + exposed1: 'string', + 'deeply.exposed2': 'boolean', + 'deeply.nested.exposed3': 'number', + 'deeply.nested.structure.exposed4': 'string', + // notExposed5, notExposed6, notExposed7, and notExposed8 are not present }, }); }); diff --git a/src/core/server/plugins/create_browser_config.ts b/src/core/server/plugins/create_browser_config.ts index 95c8de7f4c8cd..0bf812d2e5cce 100644 --- a/src/core/server/plugins/create_browser_config.ts +++ b/src/core/server/plugins/create_browser_config.ts @@ -6,19 +6,25 @@ * Side Public License, v 1. */ -import { ExposedToBrowserDescriptor } from './types'; +import { ExposedToBrowserDescriptor, PluginConfigDescriptor } from './types'; export const createBrowserConfig = ( config: T, - descriptor: ExposedToBrowserDescriptor -): unknown => { - return recursiveCreateConfig(config, descriptor); + descriptor: PluginConfigDescriptor +) => { + if (!descriptor.exposeToBrowser) { + return { browserConfig: {}, exposedConfigKeys: {} }; + } + return { + browserConfig: recursiveCreateConfig(config, descriptor.exposeToBrowser), + exposedConfigKeys: getExposedConfigKeys(descriptor), + }; }; const recursiveCreateConfig = ( config: T, descriptor: ExposedToBrowserDescriptor = {} -): unknown => { +) => { return Object.entries(config || {}).reduce((browserConfig, [key, value]) => { const exposedConfig = descriptor[key as keyof ExposedToBrowserDescriptor]; if (exposedConfig && typeof exposedConfig === 'object') { @@ -30,3 +36,39 @@ const recursiveCreateConfig = ( return browserConfig; }, {} as Record); }; + +/** + * Given a plugin descriptor, this function returns an object that contains a flattened list of exposed config keys. This is used for a CI + * check to ensure that consumers don't accidentally expose more config settings to the browser than intended. + */ +function getExposedConfigKeys(descriptor: PluginConfigDescriptor) { + const schemaStructure = descriptor.schema.getSchemaStructure(); + const flattenedConfigSchema: Record = {}; + for (const { path, type } of schemaStructure) { + if (checkIsPathExposed(path, descriptor.exposeToBrowser!)) { + flattenedConfigSchema[path.join('.')] = type; + } + } + return flattenedConfigSchema; +} + +function checkIsPathExposed( + path: string[], + descriptor: ExposedToBrowserDescriptor +) { + let isExposed = false; + for (const key of path) { + // Traverse the path to see if it is exposed or not + const exposedConfig = descriptor[key as keyof ExposedToBrowserDescriptor]; + if (exposedConfig && typeof exposedConfig === 'object') { + // @ts-expect-error Type 'undefined' is not assignable to type 'ExposedToBrowserDescriptor' + descriptor = exposedConfig; + continue; + } + if (exposedConfig === true) { + isExposed = true; + } + break; + } + return isExposed; +} diff --git a/src/core/server/plugins/plugins_service.test.ts b/src/core/server/plugins/plugins_service.test.ts index 65ef756b39e17..7c6938bdde224 100644 --- a/src/core/server/plugins/plugins_service.test.ts +++ b/src/core/server/plugins/plugins_service.test.ts @@ -1012,14 +1012,16 @@ describe('PluginsService', () => { const prebootUIConfig$ = preboot.uiPlugins.browserConfigs.get('plugin-with-expose-preboot')!; await expect(prebootUIConfig$.pipe(take(1)).toPromise()).resolves.toEqual({ - sharedProp: 'sharedProp default value plugin-with-expose-preboot', + browserConfig: { sharedProp: 'sharedProp default value plugin-with-expose-preboot' }, + exposedConfigKeys: { sharedProp: 'string' }, }); const standardUIConfig$ = standard.uiPlugins.browserConfigs.get( 'plugin-with-expose-standard' )!; await expect(standardUIConfig$.pipe(take(1)).toPromise()).resolves.toEqual({ - sharedProp: 'sharedProp default value plugin-with-expose-standard', + browserConfig: { sharedProp: 'sharedProp default value plugin-with-expose-standard' }, + exposedConfigKeys: { sharedProp: 'string' }, }); }); diff --git a/src/core/server/plugins/plugins_service.ts b/src/core/server/plugins/plugins_service.ts index f202f09735d45..2c1ba26b96ebb 100644 --- a/src/core/server/plugins/plugins_service.ts +++ b/src/core/server/plugins/plugins_service.ts @@ -231,9 +231,7 @@ export class PluginsService implements CoreService createBrowserConfig(config, configDescriptor.exposeToBrowser!)) - ), + .pipe(map((config: any) => createBrowserConfig(config, configDescriptor))), ]; }) ); diff --git a/src/core/server/rendering/rendering_service.tsx b/src/core/server/rendering/rendering_service.tsx index 9d15c7df15715..73746a8f202ff 100644 --- a/src/core/server/rendering/rendering_service.tsx +++ b/src/core/server/rendering/rendering_service.tsx @@ -80,7 +80,7 @@ export class RenderingService { { http, uiPlugins, status }: RenderOptions, request: KibanaRequest, uiSettings: IUiSettingsClient, - { isAnonymousPage = false, vars }: IRenderOptions = {} + { isAnonymousPage = false, vars, includeExposedConfigKeys }: IRenderOptions = {} ) { const env = { mode: this.coreContext.env.mode, @@ -135,11 +135,15 @@ export class RenderingService { externalUrl: http.externalUrl, vars: vars ?? {}, uiPlugins: await Promise.all( - filteredPlugins.map(async ([id, plugin]) => ({ - id, - plugin, - config: await getUiConfig(uiPlugins, id), - })) + filteredPlugins.map(async ([id, plugin]) => { + const { browserConfig, exposedConfigKeys } = await getUiConfig(uiPlugins, id); + return { + id, + plugin, + config: browserConfig, + ...(includeExposedConfigKeys && { exposedConfigKeys }), + }; + }) ), legacyMetadata: { uiSettings: settings, @@ -155,5 +159,8 @@ export class RenderingService { const getUiConfig = async (uiPlugins: UiPlugins, pluginId: string) => { const browserConfig = uiPlugins.browserConfigs.get(pluginId); - return ((await browserConfig?.pipe(take(1)).toPromise()) ?? {}) as Record; + return ((await browserConfig?.pipe(take(1)).toPromise()) ?? { + browserConfig: {}, + exposedConfigKeys: {}, + }) as { browserConfig: Record; exposedConfigKeys: Record }; }; diff --git a/src/core/server/rendering/types.ts b/src/core/server/rendering/types.ts index 121c272089a7c..2c0aafe61e018 100644 --- a/src/core/server/rendering/types.ts +++ b/src/core/server/rendering/types.ts @@ -93,6 +93,12 @@ export interface IRenderOptions { * @internal */ vars?: Record; + + /** + * @internal + * This is only used for integration tests that allow us to verify which config keys are exposed to the browser. + */ + includeExposedConfigKeys?: boolean; } /** @internal */ diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index daba13d656f52..fbd6fd1492094 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -1135,6 +1135,8 @@ export interface HttpResources { // @public export interface HttpResourcesRenderOptions { headers?: ResponseHeaders; + // @internal + includeExposedConfigKeys?: boolean; } // @public @@ -1308,6 +1310,8 @@ export interface IntervalHistogram { // @public (undocumented) export interface IRenderOptions { + // @internal + includeExposedConfigKeys?: boolean; isAnonymousPage?: boolean; // @internal @deprecated vars?: Record; diff --git a/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts b/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts index c1c929b0e2ce7..d2e4d4386cbfd 100644 --- a/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts +++ b/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts @@ -31,9 +31,9 @@ export class RenderingPlugin implements Plugin { const { isAnonymousPage } = req.query; if (isAnonymousPage) { - return res.renderAnonymousCoreApp(); + return res.renderAnonymousCoreApp({ includeExposedConfigKeys: true }); } - return res.renderCoreApp(); + return res.renderCoreApp({ includeExposedConfigKeys: true }); } ); } diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts index 4752f77f6cdae..c18e38cc1a4d6 100644 --- a/test/plugin_functional/test_suites/core_plugins/rendering.ts +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import _ from 'lodash'; import expect from '@kbn/expect'; import '../../plugins/core_provider_plugin/types'; @@ -21,6 +22,9 @@ declare global { } } +const EXPOSED_CONFIG_SETTINGS_ERROR = + 'Actual config settings exposed to the browser do not match what is expected; this assertion fails if extra settings are present and/or expected settings are missing'; + export default function ({ getService }: PluginFunctionalProviderContext) { const appsMenu = getService('appsMenu'); const browser = getService('browser'); @@ -41,6 +45,10 @@ export default function ({ getService }: PluginFunctionalProviderContext) { }); }; + const getInjectedMetadata = () => + browser.execute(() => { + return JSON.parse(document.querySelector('kbn-injected-metadata')!.getAttribute('data')!); + }); const getUserSettings = () => browser.execute(() => { return JSON.parse(document.querySelector('kbn-injected-metadata')!.getAttribute('data')!) @@ -53,9 +61,197 @@ export default function ({ getService }: PluginFunctionalProviderContext) { return window.__RENDERING_SESSION__; }); - // Talked to @dover, he aggreed we can skip these tests that are unexpectedly flaky - describe.skip('rendering service', () => { - it('renders "core" application', async () => { + describe('rendering service', () => { + it('exposes plugin config settings to authenticated users', async () => { + await navigateTo('/render/core'); + const injectedMetadata = await getInjectedMetadata(); + expect(injectedMetadata).to.not.be.empty(); + expect(injectedMetadata.uiPlugins).to.not.be.empty(); + + const actualExposedConfigKeys = []; + for (const { plugin, exposedConfigKeys } of injectedMetadata.uiPlugins) { + const configPath = Array.isArray(plugin.configPath) + ? plugin.configPath.join('.') + : plugin.configPath; + for (const [exposedConfigKey, type] of Object.entries(exposedConfigKeys)) { + actualExposedConfigKeys.push(`${configPath}.${exposedConfigKey} (${type})`); + } + } + const expectedExposedConfigKeys = [ + // NOTE: each exposed config key has its schema type at the end in "(parentheses)". The schema type comes from Joi; in particular, + // "(any)" can mean a few other data types. This is only intended to be a hint to make it easier for future reviewers to understand + // what types of config settings can be exposed to the browser. + // When plugin owners make a change that exposes additional config values, the changes will be reflected in this test assertion. + // Ensure that your change does not unintentionally expose any sensitive values! + 'console.ui.enabled (boolean)', + 'dashboard.allowByValueEmbeddables (boolean)', + 'data.autocomplete.querySuggestions.enabled (boolean)', + 'data.autocomplete.valueSuggestions.enabled (boolean)', + 'data.autocomplete.valueSuggestions.terminateAfter (duration)', + 'data.autocomplete.valueSuggestions.tiers (array)', + 'data.autocomplete.valueSuggestions.timeout (duration)', + 'data.search.aggs.shardDelay.enabled (boolean)', + 'enterpriseSearch.host (string)', + 'home.disableWelcomeScreen (boolean)', + 'map.emsFileApiUrl (string)', + 'map.emsFontLibraryUrl (string)', + 'map.emsLandingPageUrl (string)', + 'map.emsTileApiUrl (string)', + 'map.emsTileLayerId.bright (string)', + 'map.emsTileLayerId.dark (string)', + 'map.emsTileLayerId.desaturated (string)', + 'map.emsUrl (string)', + 'map.includeElasticMapsService (boolean)', + 'map.tilemap.options.attribution (string)', + 'map.tilemap.options.bounds (array)', + 'map.tilemap.options.default (boolean)', + 'map.tilemap.options.errorTileUrl (string)', + 'map.tilemap.options.maxZoom (number)', + 'map.tilemap.options.minZoom (number)', + 'map.tilemap.options.reuseTiles (boolean)', + 'map.tilemap.options.subdomains (array)', + 'map.tilemap.options.tileSize (number)', + 'map.tilemap.options.tms (boolean)', + 'map.tilemap.url (string)', + 'monitoring.kibana.collection.enabled (boolean)', + 'monitoring.kibana.collection.interval (number)', + 'monitoring.ui.ccs.enabled (boolean)', + 'monitoring.ui.container.apm.enabled (boolean)', + 'monitoring.ui.container.elasticsearch.enabled (boolean)', + 'monitoring.ui.container.logstash.enabled (boolean)', + 'monitoring.ui.enabled (boolean)', + 'monitoring.ui.min_interval_seconds (number)', + 'monitoring.ui.show_license_expiration (boolean)', + 'newsfeed.fetchInterval (duration)', + 'newsfeed.mainInterval (duration)', + 'newsfeed.service.pathTemplate (string)', + 'newsfeed.service.urlRoot (any)', + 'telemetry.allowChangingOptInStatus (boolean)', + 'telemetry.banner (boolean)', + 'telemetry.enabled (boolean)', + 'telemetry.optIn (any)', + 'telemetry.sendUsageFrom (alternatives)', + 'telemetry.sendUsageTo (any)', + 'usageCollection.uiCounters.debug (boolean)', + 'usageCollection.uiCounters.enabled (boolean)', + 'vis_type_vega.enableExternalUrls (boolean)', + 'xpack.apm.profilingEnabled (boolean)', + 'xpack.apm.serviceMapEnabled (boolean)', + 'xpack.apm.ui.enabled (boolean)', + 'xpack.apm.ui.maxTraceItems (number)', + 'xpack.apm.ui.transactionGroupBucketSize (number)', + 'xpack.cases.markdownPlugins.lens (boolean)', + 'xpack.ccr.ui.enabled (boolean)', + 'xpack.cloud.base_url (string)', + 'xpack.cloud.chat.chatURL (string)', + 'xpack.cloud.chat.enabled (boolean)', + 'xpack.cloud.cname (string)', + 'xpack.cloud.deployment_url (string)', + 'xpack.cloud.full_story.enabled (boolean)', + 'xpack.cloud.full_story.org_id (any)', + 'xpack.cloud.id (string)', + 'xpack.cloud.organization_url (string)', + 'xpack.cloud.profile_url (string)', + 'xpack.data_enhanced.search.sessions.cleanupInterval (duration)', + 'xpack.data_enhanced.search.sessions.defaultExpiration (duration)', + 'xpack.data_enhanced.search.sessions.enabled (boolean)', + 'xpack.data_enhanced.search.sessions.expireInterval (duration)', + 'xpack.data_enhanced.search.sessions.management.expiresSoonWarning (duration)', + 'xpack.data_enhanced.search.sessions.management.maxSessions (number)', + 'xpack.data_enhanced.search.sessions.management.refreshInterval (duration)', + 'xpack.data_enhanced.search.sessions.management.refreshTimeout (duration)', + 'xpack.data_enhanced.search.sessions.maxUpdateRetries (number)', + 'xpack.data_enhanced.search.sessions.monitoringTaskTimeout (duration)', + 'xpack.data_enhanced.search.sessions.notTouchedInProgressTimeout (duration)', + 'xpack.data_enhanced.search.sessions.notTouchedTimeout (duration)', + 'xpack.data_enhanced.search.sessions.pageSize (number)', + 'xpack.data_enhanced.search.sessions.trackingInterval (duration)', + 'xpack.discoverEnhanced.actions.exploreDataInChart.enabled (boolean)', + 'xpack.discoverEnhanced.actions.exploreDataInContextMenu.enabled (boolean)', + 'xpack.fleet.agents.enabled (boolean)', + 'xpack.global_search.search_timeout (duration)', + 'xpack.graph.canEditDrillDownUrls (boolean)', + 'xpack.graph.savePolicy (alternatives)', + 'xpack.ilm.ui.enabled (boolean)', + 'xpack.index_management.ui.enabled (boolean)', + 'xpack.infra.sources.default.fields.message (array)', + 'xpack.license_management.ui.enabled (boolean)', + 'xpack.maps.preserveDrawingBuffer (boolean)', + 'xpack.maps.showMapsInspectorAdapter (boolean)', + 'xpack.observability.unsafe.alertingExperience.enabled (boolean)', + 'xpack.observability.unsafe.cases.enabled (boolean)', + 'xpack.observability.unsafe.overviewNext.enabled (boolean)', + 'xpack.observability.unsafe.rules.enabled (boolean)', + 'xpack.osquery.actionEnabled (boolean)', + 'xpack.osquery.packs (boolean)', + 'xpack.osquery.savedQueries (boolean)', + 'xpack.remote_clusters.ui.enabled (boolean)', + /** + * NOTE: The Reporting plugin is currently disabled in functional tests (see test/functional/config.js). + * It will be re-enabled once #102552 is completed. + */ + // 'xpack.reporting.roles.allow (array)', + // 'xpack.reporting.roles.enabled (boolean)', + // 'xpack.reporting.poll.jobCompletionNotifier.interval (number)', + // 'xpack.reporting.poll.jobCompletionNotifier.intervalErrorMultiplier (number)', + // 'xpack.reporting.poll.jobsRefresh.interval (number)', + // 'xpack.reporting.poll.jobsRefresh.intervalErrorMultiplier (number)', + 'xpack.rollup.ui.enabled (boolean)', + 'xpack.saved_object_tagging.cache_refresh_interval (duration)', + 'xpack.security.loginAssistanceMessage (string)', + 'xpack.security.sameSiteCookies (alternatives)', + 'xpack.security.showInsecureClusterWarning (boolean)', + 'xpack.securitySolution.enableExperimental (array)', + 'xpack.snapshot_restore.slm_ui.enabled (boolean)', + 'xpack.snapshot_restore.ui.enabled (boolean)', + 'xpack.trigger_actions_ui.enableExperimental (array)', + 'xpack.trigger_actions_ui.enableGeoTrackingThresholdAlert (boolean)', + 'xpack.upgrade_assistant.readonly (boolean)', + 'xpack.upgrade_assistant.ui.enabled (boolean)', + ]; + // We don't assert that actualExposedConfigKeys and expectedExposedConfigKeys are equal, because test failure messages with large + // arrays are hard to grok. Instead, we take the difference between the two arrays and assert them separately, that way it's + // abundantly clear when the test fails that (A) Kibana is exposing a new key, or (B) Kibana is no longer exposing a key. + const extra = _.difference(actualExposedConfigKeys, expectedExposedConfigKeys).sort(); + const missing = _.difference(expectedExposedConfigKeys, actualExposedConfigKeys).sort(); + expect({ extra, missing }).to.eql({ extra: [], missing: [] }, EXPOSED_CONFIG_SETTINGS_ERROR); + }); + + it('exposes plugin config settings to unauthenticated users', async () => { + await navigateTo('/render/core?isAnonymousPage=true'); + const injectedMetadata = await getInjectedMetadata(); + expect(injectedMetadata).to.not.be.empty(); + expect(injectedMetadata.uiPlugins).to.not.be.empty(); + + const actualExposedConfigKeys = []; + for (const { plugin, exposedConfigKeys } of injectedMetadata.uiPlugins) { + const configPath = Array.isArray(plugin.configPath) + ? plugin.configPath.join('.') + : plugin.configPath; + for (const [exposedConfigKey, type] of Object.entries(exposedConfigKeys)) { + actualExposedConfigKeys.push(`${configPath}.${exposedConfigKey} (${type})`); + } + } + const expectedExposedConfigKeys = [ + // NOTE: each exposed config key has its schema type at the end in "(parentheses)". The schema type comes from Joi; in particular, + // "(any)" can mean a few other data types. This is only intended to be a hint to make it easier for future reviewers to understand + // what types of config settings can be exposed to the browser. + // When plugin owners make a change that exposes additional config values, the changes will be reflected in this test assertion. + // Ensure that your change does not unintentionally expose any sensitive values! + 'xpack.security.loginAssistanceMessage (string)', + 'xpack.security.sameSiteCookies (alternatives)', + 'xpack.security.showInsecureClusterWarning (boolean)', + ]; + // We don't assert that actualExposedConfigKeys and expectedExposedConfigKeys are equal, because test failure messages with large + // arrays are hard to grok. Instead, we take the difference between the two arrays and assert them separately, that way it's + // abundantly clear when the test fails that (A) Kibana is exposing a new key, or (B) Kibana is no longer exposing a key. + const extra = _.difference(actualExposedConfigKeys, expectedExposedConfigKeys).sort(); + const missing = _.difference(expectedExposedConfigKeys, actualExposedConfigKeys).sort(); + expect({ extra, missing }).to.eql({ extra: [], missing: [] }, EXPOSED_CONFIG_SETTINGS_ERROR); + }); + + // FLAKY + it.skip('renders "core" application', async () => { await navigateTo('/render/core'); const [loadingMessage, userSettings] = await Promise.all([ @@ -70,7 +266,8 @@ export default function ({ getService }: PluginFunctionalProviderContext) { expect(await exists('renderingHeader')).to.be(true); }); - it('renders "core" application without user settings', async () => { + // FLAKY + it.skip('renders "core" application without user settings', async () => { await navigateTo('/render/core?isAnonymousPage=true'); const [loadingMessage, userSettings] = await Promise.all([ @@ -85,7 +282,8 @@ export default function ({ getService }: PluginFunctionalProviderContext) { expect(await exists('renderingHeader')).to.be(true); }); - it('navigates between standard application and one with custom appRoute', async () => { + // FLAKY + it.skip('navigates between standard application and one with custom appRoute', async () => { await navigateTo('/'); await find.waitForElementStale(await findLoadingMessage()); @@ -108,7 +306,8 @@ export default function ({ getService }: PluginFunctionalProviderContext) { ]); }); - it('navigates between applications with custom appRoutes', async () => { + // FLAKY + it.skip('navigates between applications with custom appRoutes', async () => { await navigateTo('/'); await find.waitForElementStale(await findLoadingMessage()); From 2894e407e382fd36f3ffb725cd3a1dd5f7130bf9 Mon Sep 17 00:00:00 2001 From: Rachel Shen Date: Sun, 10 Apr 2022 10:07:49 -0600 Subject: [PATCH 21/42] [Shared UX] Migrate popover from presentation to shared ux package (#128665) * initial commit * remove styling from popover * update test * update * remove primary * updates * allow iconSide * remove unused * update * update with ts ignore * update test * missed add from lib test update * update docs * Addressing PR comments * Reintroduce ts-ignore * Apply fix for ts-error * Reintroduce ts-ignore * Updating comments Co-authored-by: Maja Grubic Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../kbn-shared-ux-components/src/index.ts | 12 +-- .../add_from_library.test.tsx.snap | 2 + .../__snapshots__/primary.test.tsx.snap | 2 + .../src/toolbar/buttons/primary/primary.mdx | 6 +- .../src/toolbar/buttons/primary/primary.tsx | 6 +- .../src/toolbar/index.ts | 1 + .../__snapshots__/popover.test.tsx.snap | 89 +++++++++++++++++++ .../src/toolbar/popovers/popover.mdx | 11 +++ .../src/toolbar/popovers/popover.stories.tsx | 70 +++++++++++++++ .../src/toolbar/popovers/popover.test.tsx | 30 +++++++ .../src/toolbar/popovers/popover.tsx | 41 +++++++++ 11 files changed, 258 insertions(+), 12 deletions(-) create mode 100644 packages/kbn-shared-ux-components/src/toolbar/popovers/__snapshots__/popover.test.tsx.snap create mode 100644 packages/kbn-shared-ux-components/src/toolbar/popovers/popover.mdx create mode 100644 packages/kbn-shared-ux-components/src/toolbar/popovers/popover.stories.tsx create mode 100644 packages/kbn-shared-ux-components/src/toolbar/popovers/popover.test.tsx create mode 100644 packages/kbn-shared-ux-components/src/toolbar/popovers/popover.tsx diff --git a/packages/kbn-shared-ux-components/src/index.ts b/packages/kbn-shared-ux-components/src/index.ts index 557ac980a14c6..a022d2d0c755d 100644 --- a/packages/kbn-shared-ux-components/src/index.ts +++ b/packages/kbn-shared-ux-components/src/index.ts @@ -10,7 +10,7 @@ import React from 'react'; import { withSuspense } from '@kbn/shared-ux-utility'; /** - * The Lazily-loaded `ExitFullScreenButton` component. Consumers should use `React.Suspennse` or the + * The Lazily-loaded `ExitFullScreenButton` component. Consumers should use `React.Suspense` or the * `withSuspense` HOC to load this component. */ export const LazyExitFullScreenButton = React.lazy(() => @@ -42,12 +42,12 @@ export const ExitFullScreenButton = withSuspense(LazyExitFullScreenButton); export const ToolbarButton = withSuspense(LazyToolbarButton); /** - * An example of the solution toolbar button + * An example of the toolbar button and popover */ -export { AddFromLibraryButton } from './toolbar'; +export { AddFromLibraryButton, ToolbarPopover } from './toolbar'; /** - * The Lazily-loaded `NoDataViews` component. Consumers should use `React.Suspennse` or the + * The Lazily-loaded `NoDataViews` component. Consumers should use `React.Suspense` or the * `withSuspense` HOC to load this component. */ export const LazyNoDataViews = React.lazy(() => @@ -64,7 +64,7 @@ export const LazyNoDataViews = React.lazy(() => export const NoDataViews = withSuspense(LazyNoDataViews); /** - * A pure `NoDataViews` component, with no services hooks. Consumers should use `React.Suspennse` or the + * A pure `NoDataViews` component, with no services hooks. Consumers should use `React.Suspense` or the * `withSuspense` HOC to load this component. */ export const LazyNoDataViewsComponent = React.lazy(() => @@ -81,7 +81,7 @@ export const LazyNoDataViewsComponent = React.lazy(() => export const NoDataViewsComponent = withSuspense(LazyNoDataViewsComponent); /** - * The Lazily-loaded `IconButtonGroup` component. Consumers should use `React.Suspennse` or the + * The Lazily-loaded `IconButtonGroup` component. Consumers should use `React.Suspense` or the * `withSuspense` HOC to load this component. */ export const LazyIconButtonGroup = React.lazy(() => diff --git a/packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/__snapshots__/add_from_library.test.tsx.snap b/packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/__snapshots__/add_from_library.test.tsx.snap index 4cdc858c7e50c..520184f0f96dc 100644 --- a/packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/__snapshots__/add_from_library.test.tsx.snap +++ b/packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/__snapshots__/add_from_library.test.tsx.snap @@ -9,6 +9,7 @@ exports[` is rendered 1`] = ` @@ -18,6 +19,7 @@ exports[` is rendered 1`] = ` disabled={false} element="button" fill={true} + iconSide="left" iconType="folderOpen" isDisabled={false} size="m" diff --git a/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/__snapshots__/primary.test.tsx.snap b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/__snapshots__/primary.test.tsx.snap index 8e447f7a0ee5c..7f50690aba8a6 100644 --- a/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/__snapshots__/primary.test.tsx.snap +++ b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/__snapshots__/primary.test.tsx.snap @@ -43,6 +43,7 @@ exports[` is rendered 1`] = ` is rendered 1`] = ` disabled={false} element="button" fill={true} + iconSide="left" isDisabled={false} size="m" type="button" diff --git a/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.mdx b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.mdx index c1fa431f39bdc..5b72eb92360be 100644 --- a/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.mdx +++ b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.mdx @@ -1,12 +1,12 @@ --- id: sharedUX/Components/ToolbarButton slug: /shared-ux/components/toolbar/buttons/primary -title: Solution Toolbar Button +title: Toolbar Button summary: An opinionated implementation of the toolbar extracted to just the button. tags: ['shared-ux', 'component'] -date: 2022-02-17 +date: 2022-03-30 --- > This documentation is in-progress. -This button is a part of the solution toolbar component. This button has primary styling and requires a label. OnClick handlers and icon types are supported as an extension of EuiButtonProps. Icons are always on the left of any labels within the button. +This button is a part of the solution toolbar component. This button has primary styling and requires a label. OnClick handlers, icon side, and icon types are supported as an extension of EuiButtonProps. Icons by default are left of any labels within the button but can also be set to right. diff --git a/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.tsx b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.tsx index 48677d965fa6c..f935a08fe8434 100644 --- a/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.tsx +++ b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.tsx @@ -10,13 +10,13 @@ import React from 'react'; import { EuiButton } from '@elastic/eui'; import { EuiButtonPropsForButton } from '@elastic/eui/src/components/button/button'; -export interface Props extends Pick { +export interface Props extends Pick { label: string; } -export const ToolbarButton = ({ label, ...rest }: Props) => { +export const ToolbarButton = ({ label, iconSide = 'left', ...rest }: Props) => { return ( - + {label} ); diff --git a/packages/kbn-shared-ux-components/src/toolbar/index.ts b/packages/kbn-shared-ux-components/src/toolbar/index.ts index 513f81c1ddfc7..f3e74ace3e5ff 100644 --- a/packages/kbn-shared-ux-components/src/toolbar/index.ts +++ b/packages/kbn-shared-ux-components/src/toolbar/index.ts @@ -8,3 +8,4 @@ export { ToolbarButton } from './buttons/primary/primary'; export { IconButtonGroup } from './buttons/icon_button_group/icon_button_group'; export { AddFromLibraryButton } from './buttons/add_from_library/add_from_library'; +export { ToolbarPopover } from './popovers/popover'; diff --git a/packages/kbn-shared-ux-components/src/toolbar/popovers/__snapshots__/popover.test.tsx.snap b/packages/kbn-shared-ux-components/src/toolbar/popovers/__snapshots__/popover.test.tsx.snap new file mode 100644 index 0000000000000..c38d0b8e4cd7c --- /dev/null +++ b/packages/kbn-shared-ux-components/src/toolbar/popovers/__snapshots__/popover.test.tsx.snap @@ -0,0 +1,89 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` is rendered 1`] = ` + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+
+ + + + + + + +
+
+
+
+`; diff --git a/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.mdx b/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.mdx new file mode 100644 index 0000000000000..d1ade51485ae4 --- /dev/null +++ b/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.mdx @@ -0,0 +1,11 @@ +--- +id: sharedUX/Components/Popover +slug: /shared-ux/components/toolbar/popovers/popover +title: Toolbar Popover +summary: A popover component that can be placed within a toolbar button. +tags: ['shared-ux', 'component'] +date: 2022-03-28 +--- + +This component is a thing wrapper around `EuiPopover` that handles open and close state. Its open and close state is controlled by a `ToolbarButton` button component. +This popover requires a label and children. \ No newline at end of file diff --git a/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.stories.tsx b/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.stories.tsx new file mode 100644 index 0000000000000..f429cebd7071b --- /dev/null +++ b/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.stories.tsx @@ -0,0 +1,70 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EuiContextMenu } from '@elastic/eui'; +import { ButtonContentIconSide } from '@elastic/eui/src/components/button/button_content'; +import { Story } from '@storybook/react'; +import React from 'react'; +import { ToolbarPopover } from './popover'; +import mdx from './popover.mdx'; + +export default { + title: 'Toolbar/Popover', + description: 'A popover that is a part of a toolbar.', + parameters: { + docs: { + page: mdx, + }, + }, + argTypes: { + iconSide: { + control: { + type: 'radio', + options: ['left', 'right', 'undefined'], + }, + }, + }, +}; + +export const Component: Story<{ + iconSide: ButtonContentIconSide | undefined; +}> = ({ iconSide }) => { + return ( + + {() => ( + + )} + + ); +}; + +Component.args = { + iconSide: 'left', +}; diff --git a/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.test.tsx b/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.test.tsx new file mode 100644 index 0000000000000..fcbf3d3542a16 --- /dev/null +++ b/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.test.tsx @@ -0,0 +1,30 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { mount as enzymeMount } from 'enzyme'; +import React from 'react'; +import { ToolbarPopover } from './popover'; + +describe('', () => { + test('is rendered', () => { + const isOpen = true; + const component = enzymeMount( !isOpen} />); + + expect(component).toMatchSnapshot(); + }); + + test('accepts an onClick handler', () => { + const isOpen = true; + const mockHandler = jest.fn(); + const component = enzymeMount( + !isOpen} onClick={mockHandler} /> + ); + component.simulate('click'); + expect(mockHandler).toHaveBeenCalled(); + }); +}); diff --git a/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.tsx b/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.tsx new file mode 100644 index 0000000000000..ceae588b61941 --- /dev/null +++ b/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.tsx @@ -0,0 +1,41 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState } from 'react'; +import { EuiPopover } from '@elastic/eui'; +import { Props as EuiPopoverProps } from '@elastic/eui/src/components/popover/popover'; + +import { ToolbarButton, Props as ButtonProps } from '../buttons/primary/primary'; + +type AllowedButtonProps = Omit; +type AllowedPopoverProps = Omit< + EuiPopoverProps, + 'button' | 'isOpen' | 'closePopover' | 'anchorPosition' +>; + +export type Props = AllowedButtonProps & + AllowedPopoverProps & { + children: (arg: { closePopover: () => void }) => React.ReactNode; + }; + +export const ToolbarPopover = ({ label, iconType, children, iconSide, ...popover }: Props) => { + const [isOpen, setIsOpen] = useState(false); + + const onButtonClick = () => setIsOpen((status) => !status); + const closePopover = () => setIsOpen(false); + + const button = ; + + return ( + // the following ts-ignore is needed until typings/* directory is exposed for consumption to packages + // @ts-ignore Types of property css are incompatible Type 'InterpolationWithTheme' is not assignable to type 'Interpolation'. + + {children({ closePopover })} + + ); +}; From 3a3d1b9ae9d52c8b44d31158f47149eb9c3159ac Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 11 Apr 2022 11:04:25 +0200 Subject: [PATCH 22/42] [Lens] Do not switch to incomplete charts on field drop if possible (#129578) * improve suggestion handling * improve tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../editor_frame/suggestion_helpers.test.ts | 35 ++++++++++++ .../editor_frame/suggestion_helpers.ts | 6 ++- .../heatmap_visualization/suggestions.test.ts | 53 +++++++++++++++++++ .../heatmap_visualization/suggestions.ts | 4 +- 4 files changed, 96 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts index cdaac370c31f1..f7ffdee236979 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts @@ -733,6 +733,41 @@ describe('suggestion helpers', () => { ); }); + it('should get the top non-hidden suggestion if there is no active visualization', () => { + defaultParams[0] = { + '1': { + getTableSpec: () => [], + datasourceId: '', + getOperationForColumnId: jest.fn(), + getVisualDefaults: jest.fn(), + getSourceId: jest.fn(), + getFilters: jest.fn(), + }, + }; + defaultParams[3] = { + testVis: mockVisualization1, + vis2: mockVisualization2, + }; + mockVisualization1.getSuggestions.mockReturnValue([]); + mockVisualization2.getSuggestions.mockReturnValue([ + { + score: 0.3, + title: 'second suggestion', + state: { second: true }, + previewIcon: 'empty', + }, + { + score: 0.5, + title: 'mop suggestion', + state: { first: true }, + previewIcon: 'empty', + hide: true, + }, + ]); + const result = getTopSuggestionForField(...defaultParams); + expect(result!.title).toEqual('second suggestion'); + }); + it('should return nothing if visualization does not produce suggestions', () => { mockVisualization1.getSuggestions.mockReturnValue([]); const result = getTopSuggestionForField(...defaultParams); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts index f7b3aca890d6a..f24158d2db501 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts @@ -289,5 +289,9 @@ export function getTopSuggestionForField( field, mainPalette, }); - return suggestions.find((s) => s.visualizationId === visualization.activeId) || suggestions[0]; + return ( + suggestions.find((s) => s.visualizationId === visualization.activeId) || + suggestions.filter((suggestion) => !suggestion.hide)[0] || + suggestions[0] + ); } diff --git a/x-pack/plugins/lens/public/heatmap_visualization/suggestions.test.ts b/x-pack/plugins/lens/public/heatmap_visualization/suggestions.test.ts index 77d3f4fdde441..dbe2d9a7c9771 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/suggestions.test.ts +++ b/x-pack/plugins/lens/public/heatmap_visualization/suggestions.test.ts @@ -357,6 +357,59 @@ describe('heatmap suggestions', () => { ]); }); + test('for tables with a single metric dimension', () => { + expect( + getSuggestions({ + table: { + layerId: 'first', + isMultiRow: true, + columns: [ + { + columnId: 'test-column', + operation: { + isBucketed: false, + dataType: 'number', + label: 'Count of records', + }, + }, + ], + changeType: 'reduced', + }, + state: { + layerId: 'first', + layerType: layerTypes.DATA, + } as HeatmapVisualizationState, + keptLayerIds: ['first'], + }) + ).toEqual([ + { + state: { + layerId: 'first', + layerType: layerTypes.DATA, + shape: 'heatmap', + valueAccessor: 'test-column', + gridConfig: { + type: HEATMAP_GRID_FUNCTION, + isCellLabelVisible: false, + isYAxisLabelVisible: true, + isXAxisLabelVisible: true, + isYAxisTitleVisible: false, + isXAxisTitleVisible: false, + }, + legend: { + isVisible: true, + position: Position.Right, + type: LEGEND_FUNCTION, + }, + }, + title: 'Heat map', + hide: true, + previewIcon: 'empty', + score: 0.3, + }, + ]); + }); + test('when at least one axis has a date histogram', () => { expect( getSuggestions({ diff --git a/x-pack/plugins/lens/public/heatmap_visualization/suggestions.ts b/x-pack/plugins/lens/public/heatmap_visualization/suggestions.ts index e1478c1605e7f..c13b6eb655e3b 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/suggestions.ts +++ b/x-pack/plugins/lens/public/heatmap_visualization/suggestions.ts @@ -59,6 +59,7 @@ export const getSuggestions: Visualization['getSugges } const isSingleBucketDimension = groups.length === 1 && metrics.length === 0; + const isOnlyMetricDimension = groups.length === 0 && metrics.length === 1; /** * Hide for: @@ -74,7 +75,8 @@ export const getSuggestions: Visualization['getSugges table.changeType === 'reduced' || table.changeType === 'reorder' || isSingleBucketDimension || - hasOnlyDatehistogramBuckets; + hasOnlyDatehistogramBuckets || + isOnlyMetricDimension; const newState: HeatmapVisualizationState = { shape: CHART_SHAPES.HEATMAP, From 688f0ab0877ff27bd23ac6e4cdc1339306c03f7d Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 11 Apr 2022 11:27:44 +0200 Subject: [PATCH 23/42] [Timelion] Remove Quandl and Graphite integration (#129581) * remove graphite and quandl integrations * remove the other thing Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- docs/management/advanced-options.asciidoc | 12 -- .../server/collectors/management/schema.ts | 9 -- .../server/collectors/management/types.ts | 2 - src/plugins/telemetry/schema/oss_plugins.json | 12 -- .../vis_types/timelion/common/constants.ts | 2 - .../vis_types/timelion/server/index.ts | 1 + .../timelion/server/lib/config_manager.ts | 11 -- .../vis_types/timelion/server/routes/run.ts | 1 - .../series_functions/fixtures/tl_config.js | 1 - .../server/series_functions/graphite.js | 87 ------------- .../server/series_functions/graphite.test.js | 52 -------- .../server/series_functions/quandl.js | 120 ------------------ .../server/series_functions/quandl.test.js | 84 ------------ .../vis_types/timelion/server/timelion.json | 6 - .../vis_types/timelion/server/ui_settings.ts | 32 ----- .../translations/translations/fr-FR.json | 12 -- .../translations/translations/ja-JP.json | 12 -- .../translations/translations/zh-CN.json | 12 -- 18 files changed, 1 insertion(+), 467 deletions(-) delete mode 100644 src/plugins/vis_types/timelion/server/series_functions/graphite.js delete mode 100644 src/plugins/vis_types/timelion/server/series_functions/graphite.test.js delete mode 100644 src/plugins/vis_types/timelion/server/series_functions/quandl.js delete mode 100644 src/plugins/vis_types/timelion/server/series_functions/quandl.test.js diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 5906f2dd5008f..a702c7d607e55 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -478,13 +478,6 @@ The default index when using the `.es()` query. [[timelion-estimefield]]`timelion:es.timefield`:: The default field containing a timestamp when using the `.es()` query. -[[timelion-graphite-url]]`timelion:graphite.url`:: -experimental:[] -Used with graphite queries, this is the URL of your graphite host -in the form https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite. This URL can -be selected from an allow-list configured in the `kibana.yml` under -`timelion.graphiteUrls`. - [[timelion-maxbuckets]]`timelion:max_buckets`:: The maximum number of buckets a single data source can return. This value is used for calculating automatic intervals in visualizations. @@ -492,11 +485,6 @@ used for calculating automatic intervals in visualizations. [[timelion-mininterval]]`timelion:min_interval`:: The smallest interval to calculate when using "auto". -[[timelion-quandlkey]]`timelion:quandl.key`:: -experimental:[] -Used with quandl queries, this is your API key from -https://www.quandl.com/[www.quandl.com]. - [[timelion-targetbuckets]]`timelion:target_buckets`:: Used for calculating automatic intervals in visualizations, this is the number of buckets to try to represent. diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index 7ad6cf697d10b..1abb7409709e0 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -10,11 +10,6 @@ import { MakeSchemaFrom } from 'src/plugins/usage_collection/server'; import { UsageStats } from './types'; export const stackManagementSchema: MakeSchemaFrom = { - // sensitive - 'timelion:quandl.key': { - type: 'keyword', - _meta: { description: 'Default value of the setting was changed.' }, - }, 'securitySolution:defaultIndex': { type: 'keyword', _meta: { description: 'Default value of the setting was changed.' }, @@ -35,10 +30,6 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'keyword', _meta: { description: 'Default value of the setting was changed.' }, }, - 'timelion:graphite.url': { - type: 'keyword', - _meta: { description: 'Default value of the setting changed.' }, - }, 'xpackDashboardMode:roles': { type: 'keyword', _meta: { description: 'Default value of the setting was changed.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index 86ca596925f76..b9d50f888fa93 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -10,13 +10,11 @@ export interface UsageStats { /** * sensitive settings */ - 'timelion:quandl.key': string; 'securitySolution:defaultIndex': string; 'securitySolution:defaultThreatIndex': string; 'securitySolution:newsFeedUrl': string; 'xpackReporting:customPdfLogo': string; 'notifications:banner': string; - 'timelion:graphite.url': string; 'xpackDashboardMode:roles': string; 'securitySolution:ipReputationLinks': string; 'banners:textContent': string; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 76079970f26bc..c6a237ee2b09c 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -7300,12 +7300,6 @@ }, "stack_management": { "properties": { - "timelion:quandl.key": { - "type": "keyword", - "_meta": { - "description": "Default value of the setting was changed." - } - }, "securitySolution:defaultIndex": { "type": "keyword", "_meta": { @@ -7336,12 +7330,6 @@ "description": "Default value of the setting was changed." } }, - "timelion:graphite.url": { - "type": "keyword", - "_meta": { - "description": "Default value of the setting changed." - } - }, "xpackDashboardMode:roles": { "type": "keyword", "_meta": { diff --git a/src/plugins/vis_types/timelion/common/constants.ts b/src/plugins/vis_types/timelion/common/constants.ts index a97bdd855107c..a49154b662c34 100644 --- a/src/plugins/vis_types/timelion/common/constants.ts +++ b/src/plugins/vis_types/timelion/common/constants.ts @@ -13,6 +13,4 @@ export const UI_SETTINGS = { TARGET_BUCKETS: 'timelion:target_buckets', MAX_BUCKETS: 'timelion:max_buckets', MIN_INTERVAL: 'timelion:min_interval', - GRAPHITE_URL: 'timelion:graphite.url', - QUANDL_KEY: 'timelion:quandl.key', }; diff --git a/src/plugins/vis_types/timelion/server/index.ts b/src/plugins/vis_types/timelion/server/index.ts index 396ef8b61c7bc..4bf47dff7076d 100644 --- a/src/plugins/vis_types/timelion/server/index.ts +++ b/src/plugins/vis_types/timelion/server/index.ts @@ -12,6 +12,7 @@ import { TimelionPlugin } from './plugin'; export const config: PluginConfigDescriptor = { schema: configSchema, + deprecations: ({ unused }) => [unused('graphiteUrls', { level: 'warning' })], }; export const plugin = (initializerContext: PluginInitializerContext) => diff --git a/src/plugins/vis_types/timelion/server/lib/config_manager.ts b/src/plugins/vis_types/timelion/server/lib/config_manager.ts index f12f7fce0668a..3ab60b0afaaed 100644 --- a/src/plugins/vis_types/timelion/server/lib/config_manager.ts +++ b/src/plugins/vis_types/timelion/server/lib/config_manager.ts @@ -7,18 +7,11 @@ */ import { PluginInitializerContext } from 'kibana/server'; -import { TypeOf } from '@kbn/config-schema'; -import { configSchema } from '../../config'; export class ConfigManager { private esShardTimeout: number = 0; - private graphiteUrls: string[] = []; constructor(config: PluginInitializerContext['config']) { - config.create>().subscribe((configUpdate) => { - this.graphiteUrls = configUpdate.graphiteUrls || []; - }); - config.legacy.globalConfig$.subscribe((configUpdate) => { this.esShardTimeout = configUpdate.elasticsearch.shardTimeout.asMilliseconds(); }); @@ -27,8 +20,4 @@ export class ConfigManager { getEsShardTimeout() { return this.esShardTimeout; } - - getGraphiteUrls() { - return this.graphiteUrls; - } } diff --git a/src/plugins/vis_types/timelion/server/routes/run.ts b/src/plugins/vis_types/timelion/server/routes/run.ts index 325c675011d13..bf829fe708854 100644 --- a/src/plugins/vis_types/timelion/server/routes/run.ts +++ b/src/plugins/vis_types/timelion/server/routes/run.ts @@ -90,7 +90,6 @@ export function runRoute( getFunction, getIndexPatternsService: () => indexPatternsService, getStartServices: core.getStartServices, - allowedGraphiteUrls: configManager.getGraphiteUrls(), esShardTimeout: configManager.getEsShardTimeout(), }); try { diff --git a/src/plugins/vis_types/timelion/server/series_functions/fixtures/tl_config.js b/src/plugins/vis_types/timelion/server/series_functions/fixtures/tl_config.js index 1b378db4ab25a..f3eb378095fc7 100644 --- a/src/plugins/vis_types/timelion/server/series_functions/fixtures/tl_config.js +++ b/src/plugins/vis_types/timelion/server/series_functions/fixtures/tl_config.js @@ -21,7 +21,6 @@ export default function () { }, esShardTimeout: moment.duration(30000), - allowedGraphiteUrls: ['https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite'], }); tlConfig.time = { diff --git a/src/plugins/vis_types/timelion/server/series_functions/graphite.js b/src/plugins/vis_types/timelion/server/series_functions/graphite.js deleted file mode 100644 index 06203c52debff..0000000000000 --- a/src/plugins/vis_types/timelion/server/series_functions/graphite.js +++ /dev/null @@ -1,87 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; -import _ from 'lodash'; -import fetch from 'node-fetch'; -import moment from 'moment'; -import Datasource from '../lib/classes/datasource'; - -export default new Datasource('graphite', { - args: [ - { - name: 'metric', // _test-data.users.*.data - types: ['string'], - help: i18n.translate('timelion.help.functions.graphite.args.metricHelpText', { - defaultMessage: 'Graphite metric to pull, e.g., {metricExample}', - values: { - metricExample: '_test-data.users.*.data', - }, - }), - }, - ], - help: i18n.translate('timelion.help.functions.graphiteHelpText', { - defaultMessage: `[experimental] Pull data from graphite. Configure your graphite server in Kibana's Advanced Settings`, - }), - fn: function graphite(args, tlConfig) { - const config = args.byName; - - const time = { - min: moment(tlConfig.time.from).format('HH:mm[_]YYYYMMDD'), - max: moment(tlConfig.time.to).format('HH:mm[_]YYYYMMDD'), - }; - const allowedUrls = tlConfig.allowedGraphiteUrls; - const configuredUrl = tlConfig.settings['timelion:graphite.url'] || allowedUrls[0]; - if (!allowedUrls.includes(configuredUrl)) { - throw new Error( - i18n.translate('timelion.help.functions.notAllowedGraphiteUrl', { - defaultMessage: `This graphite URL is not configured on the kibana.yml file. - Please configure your graphite server list in the kibana.yml file under 'timelion.graphiteUrls' and - select one from Kibana's Advanced Settings`, - }) - ); - } - - const URL = - configuredUrl + - '/render/' + - '?format=json' + - '&from=' + - time.min + - '&until=' + - time.max + - '&target=' + - config.metric; - - return fetch(URL) - .then(function (resp) { - return resp.json(); - }) - .then(function (resp) { - const list = _.map(resp, function (series) { - const data = _.map(series.datapoints, function (point) { - return [point[1] * 1000, point[0]]; - }); - return { - data: data, - type: 'series', - fit: 'nearest', // TODO make this customizable - label: series.target, - }; - }); - - return { - type: 'seriesList', - list: list, - }; - }) - .catch(function (e) { - throw e; - }); - }, -}); diff --git a/src/plugins/vis_types/timelion/server/series_functions/graphite.test.js b/src/plugins/vis_types/timelion/server/series_functions/graphite.test.js deleted file mode 100644 index db4fb35039324..0000000000000 --- a/src/plugins/vis_types/timelion/server/series_functions/graphite.test.js +++ /dev/null @@ -1,52 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -const expect = require('chai').expect; - -import fn from './graphite'; - -jest.mock('node-fetch', () => () => { - return Promise.resolve({ - json: function () { - return [ - { - target: '__beer__', - datapoints: [ - [3, 1000], - [14, 2000], - [1.5, 3000], - [92.6535, 4000], - ], - }, - ]; - }, - }); -}); - -import invoke from './helpers/invoke_series_fn.js'; - -describe('graphite', function () { - it('should wrap the graphite response up in a seriesList', function () { - return invoke(fn, []).then(function (result) { - expect(result.output.list[0].data[0][1]).to.eql(3); - expect(result.output.list[0].data[1][1]).to.eql(14); - }); - }); - - it('should convert the seconds to milliseconds', function () { - return invoke(fn, []).then(function (result) { - expect(result.output.list[0].data[1][0]).to.eql(2000 * 1000); - }); - }); - - it('should set the label to that of the graphite target', function () { - return invoke(fn, []).then(function (result) { - expect(result.output.list[0].label).to.eql('__beer__'); - }); - }); -}); diff --git a/src/plugins/vis_types/timelion/server/series_functions/quandl.js b/src/plugins/vis_types/timelion/server/series_functions/quandl.js deleted file mode 100644 index 3c209879d7a4c..0000000000000 --- a/src/plugins/vis_types/timelion/server/series_functions/quandl.js +++ /dev/null @@ -1,120 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; -import _ from 'lodash'; -import fetch from 'node-fetch'; -import moment from 'moment'; - -import Datasource from '../lib/classes/datasource'; - -export default new Datasource('quandl', { - dataSource: true, - args: [ - { - name: 'code', - types: ['string', 'null'], - help: i18n.translate('timelion.help.functions.quandl.args.codeHelpText', { - defaultMessage: 'The quandl code to plot. You can find these on quandl.com.', - }), - }, - { - name: 'position', - types: ['number', 'null'], - help: i18n.translate('timelion.help.functions.quandl.args.positionHelpText', { - defaultMessage: - 'Some quandl sources return multiple series, which one should I use? 1 based index.', - }), - }, - ], - help: i18n.translate('timelion.help.functions.quandlHelpText', { - defaultMessage: ` - [experimental] - Pull data from quandl.com using the quandl code. Set {quandlKeyField} to your free API key in Kibana's - Advanced Settings. The API has a really low rate limit without a key.`, - values: { - quandlKeyField: '"timelion:quandl.key"', - }, - }), - fn: function quandlFn(args, tlConfig) { - const intervalMap = { - '1d': 'daily', - '1w': 'weekly', - '1M': 'monthly', - '1y': 'annual', - }; - - const config = _.defaults(args.byName, { - code: 'WIKI/AAPL', - position: 1, - interval: intervalMap[tlConfig.time.interval], - apikey: tlConfig.settings['timelion:quandl.key'], - }); - - if (!config.interval) { - throw new Error( - i18n.translate('timelion.serverSideErrors.quandlFunction.unsupportedIntervalErrorMessage', { - defaultMessage: - 'quandl() unsupported interval: {interval}. quandl() supports: {intervals}', - values: { - interval: tlConfig.time.interval, - intervals: _.keys(intervalMap).join(', '), - }, - }) - ); - } - - const time = { - min: moment.utc(tlConfig.time.from).format('YYYY-MM-DD'), - max: moment.utc(tlConfig.time.to).format('YYYY-MM-DD'), - }; - - // POSITIONS - // 1. open - // 2. high - // 3. low - // 4. close - // 5. volume - - const URL = - 'https://www.quandl.com/api/v1/datasets/' + - config.code + - '.json' + - '?sort_order=asc' + - '&trim_start=' + - time.min + - '&trim_end=' + - time.max + - '&collapse=' + - config.interval + - '&auth_token=' + - config.apikey; - - return fetch(URL) - .then(function (resp) { - return resp.json(); - }) - .then(function (resp) { - const data = _.map(resp.data, function (bucket) { - return [moment(bucket[0]).valueOf(), bucket[config.position]]; - }); - - return { - type: 'seriesList', - list: [ - { - data: data, - type: 'series', - fit: 'nearest', - label: resp.name, - }, - ], - }; - }); - }, -}); diff --git a/src/plugins/vis_types/timelion/server/series_functions/quandl.test.js b/src/plugins/vis_types/timelion/server/series_functions/quandl.test.js deleted file mode 100644 index 152339c804cec..0000000000000 --- a/src/plugins/vis_types/timelion/server/series_functions/quandl.test.js +++ /dev/null @@ -1,84 +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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { parse } from 'query-string'; -import fn from './quandl'; -import moment from 'moment'; -import fetchMock from 'node-fetch'; - -const parseURL = require('url').parse; -const tlConfig = require('./fixtures/tl_config')(); - -function parseUrlParams(url) { - return parse(parseURL(url).query, { sort: false }); -} - -jest.mock('node-fetch', () => - jest.fn(() => - Promise.resolve({ - json: function () { - return { - name: '__beer__', - data: [ - ['2015-01-01', 3], - ['2015-01-02', 14], - ['2015-01-03', 15.92], - ['2015-01-04', 65.35], - ], - }; - }, - }) - ) -); - -import invoke from './helpers/invoke_series_fn.js'; - -describe('quandl', function () { - beforeEach(function () { - jest.clearAllMocks(); - }); - - it('should wrap the quandl response up in a seriesList', function () { - return invoke(fn, []).then(function (result) { - expect(result.output.list[0].data[0][1]).toEqual(3); - expect(result.output.list[0].data[1][1]).toEqual(14); - }); - }); - - it('should set the label to that of the quandl name', function () { - return invoke(fn, []).then(function (result) { - expect(result.output.list[0].label).toEqual('__beer__'); - }); - }); - - it('should call the quandl API with the quandl code that has been passed', function () { - return invoke(fn, ['BEER/IS_GOOD']).then(function () { - expect(fetchMock).toHaveBeenCalled(); - expect(fetchMock.mock.calls[0][0].match(/datasets\/(.*).json/)[1]).toEqual('BEER/IS_GOOD'); - }); - }); - - it('should limit the time span and interval to the stuff attached to tlConfig', function () { - return invoke(fn, []).then(function () { - const params = parseUrlParams(fetchMock.mock.calls[0][0]); - expect(params.trim_start).toEqual(moment.utc(tlConfig.time.from).format('YYYY-MM-DD')); - expect(params.trim_end).toEqual(moment.utc(tlConfig.time.to).format('YYYY-MM-DD')); - }); - }); - - it('should throw an error is passed an unsupported interval', function () { - return expect(invoke(fn, [], { time: { interval: '2d' } })).rejects.toThrowError(); - }); - - it('should use the configured API key when talking to quandl', function () { - return invoke(fn, [], { settings: { 'timelion:quandl.key': 'bEeR' } }).then(function () { - const params = parseUrlParams(fetchMock.mock.calls[0][0]); - expect(params.auth_token).toEqual('bEeR'); - }); - }); -}); diff --git a/src/plugins/vis_types/timelion/server/timelion.json b/src/plugins/vis_types/timelion/server/timelion.json index ec3db84e447a3..c3f5ded2fcb78 100644 --- a/src/plugins/vis_types/timelion/server/timelion.json +++ b/src/plugins/vis_types/timelion/server/timelion.json @@ -1,14 +1,8 @@ { - "quandl": { - "key": "someKeyHere" - }, "es": { "timefield": "@timestamp", "default_index": "_all" }, - "graphite": { - "url": "https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite" - }, "default_rows": 2, "default_columns": 2, "max_buckets": 2000, diff --git a/src/plugins/vis_types/timelion/server/ui_settings.ts b/src/plugins/vis_types/timelion/server/ui_settings.ts index a79385be08531..30c170b135da8 100644 --- a/src/plugins/vis_types/timelion/server/ui_settings.ts +++ b/src/plugins/vis_types/timelion/server/ui_settings.ts @@ -12,10 +12,6 @@ import type { UiSettingsParams } from 'kibana/server'; import { UI_SETTINGS } from '../common/constants'; -const experimentalLabel = i18n.translate('timelion.uiSettings.experimentalLabel', { - defaultMessage: 'technical preview', -}); - export function getUiSettings(): Record> { return { [UI_SETTINGS.LEGACY_CHARTS_LIBRARY]: { @@ -94,33 +90,5 @@ export function getUiSettings(): Record> { category: ['timelion'], schema: schema.string(), }, - [UI_SETTINGS.GRAPHITE_URL]: { - name: i18n.translate('timelion.uiSettings.graphiteURLLabel', { - defaultMessage: 'Graphite URL', - description: - 'The URL should be in the form of https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite', - }), - value: '', - description: i18n.translate('timelion.uiSettings.graphiteURLDescription', { - defaultMessage: - '{experimentalLabel} The URL of your graphite host. If no URL is set, the first graphite URL configured in kibana.yml is used.', - values: { experimentalLabel: `[${experimentalLabel}]` }, - }), - category: ['timelion'], - schema: schema.nullable(schema.string()), - }, - [UI_SETTINGS.QUANDL_KEY]: { - name: i18n.translate('timelion.uiSettings.quandlKeyLabel', { - defaultMessage: 'Quandl key', - }), - value: 'someKeyHere', - description: i18n.translate('timelion.uiSettings.quandlKeyDescription', { - defaultMessage: '{experimentalLabel} Your API key from www.quandl.com', - values: { experimentalLabel: `[${experimentalLabel}]` }, - }), - sensitive: true, - category: ['timelion'], - schema: schema.string(), - }, }; } diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 5fe2512e96ee9..8d800c8ba38aa 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -4294,8 +4294,6 @@ "timelion.help.functions.firstHelpText": "Il s'agit d'une fonction interne qui renvoie simplement la liste de séries d'entrée. Ne l'utilisez pas.", "timelion.help.functions.fit.args.modeHelpText": "L'algorithme à utiliser pour adapter les séries à la cible. L'une des options suivantes : {fitFunctions}.", "timelion.help.functions.fitHelpText": "Remplit les valeurs nulles à l'aide d'une fonction fit définie.", - "timelion.help.functions.graphite.args.metricHelpText": "Indicateur Graphite à extraire, par ex. {metricExample}", - "timelion.help.functions.graphiteHelpText": "[expérimental] Extrayez des données de Graphite. Configurez votre serveur Graphite dans les paramètres avancés de Kibana.", "timelion.help.functions.hide.args.hideHelpText": "Masquer ou afficher les séries", "timelion.help.functions.hideHelpText": "Masquer les séries par défaut", "timelion.help.functions.holt.args.alphaHelpText": "\n Pondération de lissage de 0 à 1.\n Augmentez l’alpha pour que la nouvelle série suive de plus près l'originale.\n Diminuez-le pour rendre la série plus lisse.", @@ -4337,7 +4335,6 @@ "timelion.help.functions.movingstdHelpText": "Calculez l'écart-type mobile pour une fenêtre donnée. Utilise l'algorithme naïf en deux passes. Les erreurs d'arrondi peuvent devenir plus évidentes avec les séries très longues ou celles comportant de très grands nombres.", "timelion.help.functions.multiply.args.multiplierHelpText": "Nombre de séries par lequel multiplier. Une liste de plusieurs séries sera appliquée pour l'étiquette.", "timelion.help.functions.multiplyHelpText": "Multiplie les valeurs d'une ou de plusieurs séries d'une liste de séries à chaque position, dans chaque série, de la liste de séries d'entrée.", - "timelion.help.functions.notAllowedGraphiteUrl": "Cette URL Graphite n'est pas configurée dans le fichier kibana.yml.\n Veuillez configurer votre liste de serveurs Graphite dans le fichier kibana.yml, sous \"timelion.graphiteUrls\", puis\n en sélectionner un dans les paramètres avancés de Kibana.", "timelion.help.functions.points.args.fillColorHelpText": "Couleur à utiliser pour remplir le point", "timelion.help.functions.points.args.fillHelpText": "Nombre compris entre 0 et 10 représentant l'opacité du remplissage", "timelion.help.functions.points.args.radiusHelpText": "Taille des points", @@ -4349,9 +4346,6 @@ "timelion.help.functions.precisionHelpText": "Le nombre de chiffres à garder lors de la troncature de la partie décimale de la valeur", "timelion.help.functions.props.args.globalHelpText": "Définir des propositions sur la liste de séries plutôt que sur chaque série", "timelion.help.functions.propsHelpText": "À utiliser à vos risques et périls ; définit des propriétés arbitraires sur la série. Par exemple : {example}", - "timelion.help.functions.quandl.args.codeHelpText": "Le code Quandl à tracer. Disponible sur quandl.com.", - "timelion.help.functions.quandl.args.positionHelpText": "Certaines sources Quandl renvoient plusieurs séries. Laquelle utiliser ? Index basé sur 1.", - "timelion.help.functions.quandlHelpText": "\n [expérimental]\n Extrayez des données de quandl.com à l'aide du code Quandl. Définissez {quandlKeyField} sur votre clé d'API gratuite dans\n les paramètres avancés de Kibana. La limite de taux de l'API est très basse sans clé.", "timelion.help.functions.range.args.maxHelpText": "Nouvelle valeur maximale", "timelion.help.functions.range.args.minHelpText": "Nouvelle valeur minimale", "timelion.help.functions.rangeHelpText": "Modifie le maximum et le minimum d'une série sans changer la forme.", @@ -4401,7 +4395,6 @@ "timelion.serverSideErrors.movingaverageFunction.notValidPositionErrorMessage": "Les positions valides sont : {validPositions}.", "timelion.serverSideErrors.movingstdFunction.notValidPositionErrorMessage": "Les positions valides sont : {validPositions}.", "timelion.serverSideErrors.pointsFunction.notValidSymbolErrorMessage": "Les symboles valides sont : {validSymbols}.", - "timelion.serverSideErrors.quandlFunction.unsupportedIntervalErrorMessage": "Intervalle non pris en charge par quandl() : {interval}. Les intervalles pris en charge par quandl() sont les suivants : {intervals}.", "timelion.serverSideErrors.sheetParseErrorMessage": "Attendu : {expectedDescription} au caractère {column}", "timelion.serverSideErrors.unknownArgumentErrorMessage": "Argument inconnu pour {functionName} : {argumentName}", "timelion.serverSideErrors.unknownArgumentTypeErrorMessage": "Type d'argument non pris en charge : {argument}", @@ -4412,9 +4405,6 @@ "timelion.timelionDescription": "Affichez des données temporelles sur un graphe.", "timelion.uiSettings.defaultIndexDescription": "Index Elasticsearch par défaut dans lequel rechercher avec {esParam}", "timelion.uiSettings.defaultIndexLabel": "Index par défaut", - "timelion.uiSettings.experimentalLabel": "expérimental", - "timelion.uiSettings.graphiteURLDescription": "{experimentalLabel} L'URL de l'hôte Graphite", - "timelion.uiSettings.graphiteURLLabel": "URL Graphite", "timelion.uiSettings.legacyChartsLibraryDeprication": "Ce paramètre est déclassé et ne sera plus pris en charge à partir de la version 8.0.", "timelion.uiSettings.legacyChartsLibraryDescription": "Active la bibliothèque de graphiques héritée pour les graphiques Timelion dans Visualize.", "timelion.uiSettings.legacyChartsLibraryLabel": "Bibliothèque de graphiques Timelion héritée", @@ -4422,8 +4412,6 @@ "timelion.uiSettings.maximumBucketsLabel": "Nombre maximal de compartiments", "timelion.uiSettings.minimumIntervalDescription": "Le plus petit intervalle qui sera calculé lors de l'utilisation de l'option \"auto\"", "timelion.uiSettings.minimumIntervalLabel": "Intervalle minimum", - "timelion.uiSettings.quandlKeyDescription": "{experimentalLabel} Votre clé d'API de www.quandl.com", - "timelion.uiSettings.quandlKeyLabel": "Clé Quandl", "timelion.uiSettings.targetBucketsDescription": "Le nombre de compartiments visé lors de l'utilisation d'intervalles automatiques", "timelion.uiSettings.targetBucketsLabel": "Compartiments cibles", "timelion.uiSettings.timeFieldDescription": "Champ par défaut contenant un horodatage lors de l'utilisation de {esParam}", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index df04c6e4cf4c1..641d7debc580e 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -5035,8 +5035,6 @@ "timelion.help.functions.firstHelpText": "これは単純に input seriesList を返す内部機能です。この機能は使わないでください", "timelion.help.functions.fit.args.modeHelpText": "数列をターゲットに合わせるためのアルゴリズムです。次のいずれかです。{fitFunctions}", "timelion.help.functions.fitHelpText": "定義された fit 関数を使用して空値を入力します", - "timelion.help.functions.graphite.args.metricHelpText": "取得する Graphite メトリック、例:{metricExample}", - "timelion.help.functions.graphiteHelpText": "[実験的] Graphite からデータを取得します。Kibana の高度な設定で Graphite サーバーを構成します", "timelion.help.functions.hide.args.hideHelpText": "数列の表示と非表示を切り替えます", "timelion.help.functions.hideHelpText": "デフォルトで数列を非表示にします", "timelion.help.functions.holt.args.alphaHelpText": "\n 0 から 1 の平滑化加重です。\n アルファを上げると新しい数列がオリジナルにさらに近くなります。\n 下げると数列がスムーズになります", @@ -5078,7 +5076,6 @@ "timelion.help.functions.movingstdHelpText": "特定期間の移動標準偏差を計算します。ネイティブ two-pass アルゴリズムを使用します。非常に長い数列や、非常に大きな数字を含む数列では、四捨五入による誤差がより明らかになる可能性があります。", "timelion.help.functions.multiply.args.multiplierHelpText": "掛ける数字または数列です。複数数列を含む seriesList はラベルに適用されます。", "timelion.help.functions.multiplyHelpText": "seriesList の 1 つまたは複数の数列の値をインプット seriesList の各数列のそれぞれの配置に掛けます。", - "timelion.help.functions.notAllowedGraphiteUrl": "この Graphite URL は kibana.yml ファイルで構成されていません。\n 「timelion.graphiteUrls」で kibana.yml ファイルの Graphite サーバーリストを構成し、\n Kibana の高度な設定でいずれかを選択してください", "timelion.help.functions.points.args.fillColorHelpText": "点を塗りつぶす色です。", "timelion.help.functions.points.args.fillHelpText": "塗りつぶしの透明度を表す 0 から 10 までの数字です", "timelion.help.functions.points.args.radiusHelpText": "点のサイズです", @@ -5090,9 +5087,6 @@ "timelion.help.functions.precisionHelpText": "値の小数点以下を切り捨てる桁数です", "timelion.help.functions.props.args.globalHelpText": "各数列に対し、seriesList にプロップを設定します", "timelion.help.functions.propsHelpText": "数列に任意のプロパティを設定するため、自己責任で行ってください。例:{example}。", - "timelion.help.functions.quandl.args.codeHelpText": "プロットする Quandl コードです。これらは quandl.com に掲載されています。", - "timelion.help.functions.quandl.args.positionHelpText": "Quandl ソースによっては、複数数列を返すものがあります。どれを使用しますか?1 ベースインデックス", - "timelion.help.functions.quandlHelpText": "\n [実験的]\n Quandl コードで quandl.com からデータを取得します。Kibana で {quandlKeyField} を空き API キーに設定\n 高度な設定API は、キーなしでは非常に低いレート制限があります。", "timelion.help.functions.range.args.maxHelpText": "新しい最高値です", "timelion.help.functions.range.args.minHelpText": "新しい最低値です", "timelion.help.functions.rangeHelpText": "同じシェイプを維持しつつ数列の最高値と最低値を変更します", @@ -5143,7 +5137,6 @@ "timelion.serverSideErrors.movingaverageFunction.notValidPositionErrorMessage": "有効な配置:{validPositions}", "timelion.serverSideErrors.movingstdFunction.notValidPositionErrorMessage": "有効な配置:{validPositions}", "timelion.serverSideErrors.pointsFunction.notValidSymbolErrorMessage": "有効なシンボル:{validSymbols}", - "timelion.serverSideErrors.quandlFunction.unsupportedIntervalErrorMessage": "quandl()でサポートされていない間隔:{interval}. quandl()でサポートされている間隔:{intervals}", "timelion.serverSideErrors.sheetParseErrorMessage": "予想:文字 {column} で {expectedDescription}", "timelion.serverSideErrors.unknownArgumentErrorMessage": "{functionName} への不明な引数:{argumentName}", "timelion.serverSideErrors.unknownArgumentTypeErrorMessage": "引数タイプがサポートされていません:{argument}", @@ -5154,9 +5147,6 @@ "timelion.timelionDescription": "グラフに時系列データを表示します。", "timelion.uiSettings.defaultIndexDescription": "{esParam} で検索するデフォルトの Elasticsearch インデックスです", "timelion.uiSettings.defaultIndexLabel": "デフォルトのインデックス", - "timelion.uiSettings.experimentalLabel": "テクニカルプレビュー", - "timelion.uiSettings.graphiteURLDescription": "{experimentalLabel} Graphite ホストの URL", - "timelion.uiSettings.graphiteURLLabel": "Graphite URL", "timelion.uiSettings.legacyChartsLibraryDeprication": "この設定はサポートが終了し、将来のバージョンではサポートされません。", "timelion.uiSettings.legacyChartsLibraryDescription": "VisualizeでTimelionグラフのレガシーグラフライブラリを有効にします", "timelion.uiSettings.legacyChartsLibraryLabel": "Timelionレガシーグラフライブラリ", @@ -5164,8 +5154,6 @@ "timelion.uiSettings.maximumBucketsLabel": "バケットの最大数", "timelion.uiSettings.minimumIntervalDescription": "「auto」を使用時に計算される最小の間隔です", "timelion.uiSettings.minimumIntervalLabel": "最低間隔", - "timelion.uiSettings.quandlKeyDescription": "{experimentalLabel} www.quandl.com からの API キーです", - "timelion.uiSettings.quandlKeyLabel": "Quandl キー", "timelion.uiSettings.targetBucketsDescription": "自動間隔の使用時に目標となるバケット数です。", "timelion.uiSettings.targetBucketsLabel": "目標バケット数", "timelion.uiSettings.timeFieldDescription": "{esParam} の使用時にタイムスタンプを含むデフォルトのフィールドです", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index dd4b6d7f4bb75..059e2f68cd5f2 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -5043,8 +5043,6 @@ "timelion.help.functions.firstHelpText": "这是内部函数,只返回输入 seriesList。请勿使用", "timelion.help.functions.fit.args.modeHelpText": "用于将序列拟合到目标的算法。以下之一:{fitFunctions}", "timelion.help.functions.fitHelpText": "使用已定义的拟合函数填充 null 值", - "timelion.help.functions.graphite.args.metricHelpText": "要拉取的 Graphite 指标,例如 {metricExample}", - "timelion.help.functions.graphiteHelpText": "[实验性] 从 Graphite 拉取数据。在 Kibana 的“高级设置”中配置 Graphite 服务器", "timelion.help.functions.hide.args.hideHelpText": "隐藏或取消隐藏序列", "timelion.help.functions.hideHelpText": "默认隐藏序列", "timelion.help.functions.holt.args.alphaHelpText": "\n 平滑权重,0 到 1。\n 增加 alpha 值会使新序列更接近原始序列。\n 降低 alpha 值会使序列更平滑", @@ -5086,7 +5084,6 @@ "timelion.help.functions.movingstdHelpText": "计算特定时间窗的移动标准偏差。使用朴素的扫描两遍算法。对于极长的序列,或含有极大数的序列,舍入误差可能会更明显。", "timelion.help.functions.multiply.args.multiplierHelpText": "要乘以的数字或序列。具有多个序列的 seriesList 以标签方式进行应用。", "timelion.help.functions.multiplyHelpText": "将 seriesList 中一个或多个序列的值乘以输入 seriesList 的每个序列中的每个位置", - "timelion.help.functions.notAllowedGraphiteUrl": "在 kibana.yml 文件中未配置此 Graphite URL。\n 请在 kibana.yml 文件中“timelion.graphiteUrls”下配置 Graphite 服务器列表,并\n 从 Kibana 的“高级设置”中选择一个", "timelion.help.functions.points.args.fillColorHelpText": "用于填充点的颜色", "timelion.help.functions.points.args.fillHelpText": "介于 0 到 10 之间的数字,代表填充的不透明度", "timelion.help.functions.points.args.radiusHelpText": "点的大小", @@ -5098,9 +5095,6 @@ "timelion.help.functions.precisionHelpText": "将值的小数部分截断至的位数", "timelion.help.functions.props.args.globalHelpText": "在 seriesList 与每个序列上设置属性", "timelion.help.functions.propsHelpText": "在序列上可设置任意属性,但请自担风险。例如 {example}", - "timelion.help.functions.quandl.args.codeHelpText": "用于绘图的 quandl 代码。可以在 quandl.com 找到这些内容。", - "timelion.help.functions.quandl.args.positionHelpText": "某些 quandl 源代码会返回多个序列,我该使用哪一个?从 1 开始 的索引。", - "timelion.help.functions.quandlHelpText": "\n [实验性]\n 使用 quandl 代码从 quandl.com 拉取数据。在 Kibana 的“高级设置”中将 {quandlKeyField} 设置为\n 免费的 API 密钥。API 在没有密钥的情况下,会有非常低的速率限制。", "timelion.help.functions.range.args.maxHelpText": "新的最大值", "timelion.help.functions.range.args.minHelpText": "新的最小值", "timelion.help.functions.rangeHelpText": "保持形状不变的同时更改序列的最大值和最小值", @@ -5151,7 +5145,6 @@ "timelion.serverSideErrors.movingaverageFunction.notValidPositionErrorMessage": "有效位置为:{validPositions}", "timelion.serverSideErrors.movingstdFunction.notValidPositionErrorMessage": "有效位置为:{validPositions}", "timelion.serverSideErrors.pointsFunction.notValidSymbolErrorMessage": "有效符号为:{validSymbols}", - "timelion.serverSideErrors.quandlFunction.unsupportedIntervalErrorMessage": "quandl() 不支持的时间间隔:{interval}。quandl() 支持:{intervals}", "timelion.serverSideErrors.sheetParseErrorMessage": "应为:字符位置 {column} 的{expectedDescription}", "timelion.serverSideErrors.unknownArgumentErrorMessage": "{functionName} 的未知参数:{argumentName}", "timelion.serverSideErrors.unknownArgumentTypeErrorMessage": "不支持的参数类型:{argument}", @@ -5162,9 +5155,6 @@ "timelion.timelionDescription": "在图表上显示时间序列数据。", "timelion.uiSettings.defaultIndexDescription": "要使用 {esParam} 搜索的默认 Elasticsearch 索引", "timelion.uiSettings.defaultIndexLabel": "默认索引", - "timelion.uiSettings.experimentalLabel": "技术预览", - "timelion.uiSettings.graphiteURLDescription": "{experimentalLabel} Graphite 主机的 URL", - "timelion.uiSettings.graphiteURLLabel": "Graphite URL", "timelion.uiSettings.legacyChartsLibraryDeprication": "此设置已过时,在未来版本中将不受支持。", "timelion.uiSettings.legacyChartsLibraryDescription": "在 Visualize 中启用 timelion 图表的旧版图表库", "timelion.uiSettings.legacyChartsLibraryLabel": "Timelion 旧版图表库", @@ -5172,8 +5162,6 @@ "timelion.uiSettings.maximumBucketsLabel": "最大存储桶数", "timelion.uiSettings.minimumIntervalDescription": "使用“auto”时将计算的最小时间间隔", "timelion.uiSettings.minimumIntervalLabel": "最小时间间隔", - "timelion.uiSettings.quandlKeyDescription": "{experimentalLabel} 来自 www.quandl.com 的 API 密钥", - "timelion.uiSettings.quandlKeyLabel": "Quandl 密钥", "timelion.uiSettings.targetBucketsDescription": "使用自动时间间隔时想要的存储桶数目", "timelion.uiSettings.targetBucketsLabel": "目标存储桶", "timelion.uiSettings.timeFieldDescription": "使用 {esParam} 时包含时间戳的默认字段", From 73fb70b07290e321fcc6ec7857841534d8201ecd Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 11 Apr 2022 11:44:16 +0200 Subject: [PATCH 24/42] [Discover] Catch potental missing meta property of filter (#129183) * Catching a potential error caused by a broken filter object without meta property --- .../components/layout/discover_layout.tsx | 8 ++- .../main/components/layout/utils.test.ts | 51 +++++++++++++++++++ .../main/components/layout/utils.ts | 15 ++++++ 3 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 src/plugins/discover/public/application/main/components/layout/utils.test.ts create mode 100644 src/plugins/discover/public/application/main/components/layout/utils.ts diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index 8be5e2b7ce4be..f8ea65a58b3a9 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -25,7 +25,7 @@ import { useDiscoverServices } from '../../../../utils/use_discover_services'; import { DiscoverNoResults } from '../no_results'; import { LoadingSpinner } from '../loading_spinner/loading_spinner'; import { generateFilters } from '../../../../../../data/public'; -import { DataViewField } from '../../../../../../data_views/public'; +import { DataView, DataViewField, DataViewType } from '../../../../../../data_views/public'; import { DiscoverSidebarResponsive } from '../sidebar'; import { DiscoverLayoutProps } from './types'; import { SEARCH_FIELDS_FROM_SOURCE, SHOW_FIELD_STATISTICS } from '../../../../../common'; @@ -48,7 +48,7 @@ import { import { FieldStatisticsTable } from '../field_stats_table'; import { VIEW_MODE } from '../../../../components/view_mode_toggle'; import { DOCUMENTS_VIEW_CLICK, FIELD_STATISTICS_VIEW_CLICK } from '../field_stats_table/constants'; -import { DataViewType, DataView } from '../../../../../../data_views/public'; +import { hasActiveFilter } from './utils'; /** * Local storage key for sidebar persistence state @@ -293,9 +293,7 @@ export function DiscoverLayout({ data={data} error={dataState.error} hasQuery={!!state.query?.query} - hasFilters={ - state.filters && state.filters.filter((f) => !f.meta.disabled).length > 0 - } + hasFilters={hasActiveFilter(state.filters)} onDisableFilters={onDisableFilters} /> )} diff --git a/src/plugins/discover/public/application/main/components/layout/utils.test.ts b/src/plugins/discover/public/application/main/components/layout/utils.test.ts new file mode 100644 index 0000000000000..2201ff3dfe476 --- /dev/null +++ b/src/plugins/discover/public/application/main/components/layout/utils.test.ts @@ -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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { Filter } from '@kbn/es-query'; +import { hasActiveFilter } from './utils'; + +const testFilter: Filter = { + meta: { + alias: null, + disabled: false, + negate: false, + }, + query: { query: 'hi' }, +}; +const testFilterDisabled: Filter = { + meta: { + alias: null, + disabled: true, + negate: false, + }, + query: { query: 'hi' }, +}; + +const testFilterBroken = {} as Filter; + +describe('hasActiveFilter', () => { + test('only active filters', () => { + const filters = [testFilter]; + const result = hasActiveFilter(filters); + expect(result).toBe(true); + }); + test('only disabled filters', () => { + const filters = [testFilterDisabled]; + const result = hasActiveFilter(filters); + expect(result).toBe(false); + }); + test('disabled and active filters', () => { + const filters = [testFilter, testFilterDisabled]; + const result = hasActiveFilter(filters); + expect(result).toBe(true); + }); + test('broken filter - edge case', () => { + const filters = [testFilterBroken]; + const result = hasActiveFilter(filters); + expect(result).toBe(true); + }); +}); diff --git a/src/plugins/discover/public/application/main/components/layout/utils.ts b/src/plugins/discover/public/application/main/components/layout/utils.ts new file mode 100644 index 0000000000000..bcf9dbb6178ec --- /dev/null +++ b/src/plugins/discover/public/application/main/components/layout/utils.ts @@ -0,0 +1,15 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { Filter } from '@kbn/es-query'; + +/** + * Returns if true there's at least 1 active filter + */ +export function hasActiveFilter(filters: Filter[] | undefined) { + return filters && filters.filter((f) => !f.meta?.disabled).length > 0; +} From 3fe51eb4278bf03678143ded15f5c892402cddde Mon Sep 17 00:00:00 2001 From: Jordan <51442161+JordanSh@users.noreply.github.com> Date: Mon, 11 Apr 2022 13:30:33 +0300 Subject: [PATCH 25/42] [Cloud Posture] Csp dashboard trendline (#129481) --- .../common/constants.ts | 1 - .../cloud_security_posture/common/types.ts | 6 + .../cloud_posture_score_chart.tsx | 79 +++++++---- .../dashboard_sections/benchmarks_section.tsx | 1 + .../dashboard_sections/summary_section.tsx | 1 + .../compliance_dashboard.ts | 22 ++- .../compliance_dashboard/get_clusters.ts | 10 +- .../compliance_dashboard/get_trends.test.ts | 131 ++++++++++++++++++ .../routes/compliance_dashboard/get_trends.ts | 73 ++++++++++ 9 files changed, 289 insertions(+), 35 deletions(-) create mode 100644 x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_trends.test.ts create mode 100644 x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_trends.ts diff --git a/x-pack/plugins/cloud_security_posture/common/constants.ts b/x-pack/plugins/cloud_security_posture/common/constants.ts index be4ba273c5408..37cddf0eba9a5 100644 --- a/x-pack/plugins/cloud_security_posture/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/common/constants.ts @@ -27,7 +27,6 @@ export const RULE_FAILED = `failed`; // activated via a simple code change in a single location. export const INTERNAL_FEATURE_FLAGS = { showBenchmarks: false, - showTrendLineMock: false, showManageRulesMock: false, showRisksMock: false, } as const; diff --git a/x-pack/plugins/cloud_security_posture/common/types.ts b/x-pack/plugins/cloud_security_posture/common/types.ts index b414b49ef30d3..b9403128220f5 100644 --- a/x-pack/plugins/cloud_security_posture/common/types.ts +++ b/x-pack/plugins/cloud_security_posture/common/types.ts @@ -25,6 +25,10 @@ export interface ResourceType extends FindingsEvaluation { name: string; } +export interface PostureTrend extends Stats { + timestamp: string; +} + export interface Cluster { meta: { clusterId: string; @@ -33,12 +37,14 @@ export interface Cluster { }; stats: Stats; resourcesTypes: ResourceType[]; + trend: PostureTrend[]; } export interface ComplianceDashboardData { stats: Stats; resourcesTypes: ResourceType[]; clusters: Cluster[]; + trend: PostureTrend[]; } export interface Benchmark { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/cloud_posture_score_chart.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/cloud_posture_score_chart.tsx index 980e85f66e3f5..dd872b49aec62 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/cloud_posture_score_chart.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/cloud_posture_score_chart.tsx @@ -19,23 +19,27 @@ import { timeFormatter, } from '@elastic/charts'; import { EuiFlexGroup, EuiText, EuiHorizontalRule, EuiFlexItem } from '@elastic/eui'; +import { FormattedDate, FormattedTime } from '@kbn/i18n-react'; +import moment from 'moment'; import { statusColors } from '../../../common/constants'; -import type { Stats } from '../../../../common/types'; +import type { PostureTrend, Stats } from '../../../../common/types'; import * as TEXT from '../translations'; import { CompactFormattedNumber } from '../../../components/compact_formatted_number'; -import { INTERNAL_FEATURE_FLAGS } from '../../../../common/constants'; interface CloudPostureScoreChartProps { + trend: PostureTrend[]; data: Stats; id: string; partitionOnElementClick: (elements: PartitionElementEvent[]) => void; } +const getPostureScorePercentage = (postureScore: number): string => `${Math.round(postureScore)}%`; + const ScoreChart = ({ data: { totalPassed, totalFailed }, id, partitionOnElementClick, -}: CloudPostureScoreChartProps) => { +}: Omit) => { const data = [ { label: TEXT.PASSED, value: totalPassed }, { label: TEXT.FAILED, value: totalFailed }, @@ -79,7 +83,7 @@ const PercentageInfo = ({ totalPassed, totalFindings, }: CloudPostureScoreChartProps['data']) => { - const percentage = `${Math.round(postureScore)}%`; + const percentage = getPostureScorePercentage(postureScore); return ( @@ -94,32 +98,53 @@ const PercentageInfo = ({ ); }; -const mockData = [ - [0, 9], - [1000, 70], - [2000, 40], - [4000, 90], - [5000, 53], -]; +const convertTrendToEpochTime = (trend: PostureTrend) => ({ + ...trend, + timestamp: moment(trend.timestamp).valueOf(), +}); -const ComplianceTrendChart = () => ( - - - - - - -); +const ComplianceTrendChart = ({ trend }: { trend: PostureTrend[] }) => { + const epochTimeTrend = trend.map(convertTrendToEpochTime); + + return ( + + ( + <> + + {', '} + + + ), + }} + /> + + + getPostureScorePercentage(rawScore)} + /> + + ); +}; export const CloudPostureScoreChart = ({ data, + trend, id, partitionOnElementClick, }: CloudPostureScoreChartProps) => ( @@ -136,7 +161,7 @@ export const CloudPostureScoreChart = ({ - + ); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx index 565811bca7ee7..a3b32ef3fe2c6 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx @@ -100,6 +100,7 @@ export const BenchmarksSection = ({ handleElementClick(cluster.meta.clusterId, elements) } diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx index fa2e4e872034c..c123459d9e963 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx @@ -50,6 +50,7 @@ export const SummarySection = ({ complianceData }: { complianceData: ComplianceD diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts index 154c2d58cd330..99046d38dfcc6 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts @@ -17,9 +17,10 @@ import type { ComplianceDashboardData } from '../../../common/types'; import { CSP_KUBEBEAT_INDEX_PATTERN, STATS_ROUTE_PATH } from '../../../common/constants'; import { CspAppContext } from '../../plugin'; import { getResourcesTypes } from './get_resources_types'; -import { getClusters } from './get_clusters'; +import { ClusterWithoutTrend, getClusters } from './get_clusters'; import { getStats } from './get_stats'; import { CspRouter } from '../../types'; +import { getTrends, Trends } from './get_trends'; export interface ClusterBucket { ordered_top_hits: AggregationsTopHitsAggregate; @@ -74,6 +75,18 @@ const getLatestCyclesIds = async (esClient: ElasticsearchClient): Promise + clustersWithoutTrends.map((cluster) => ({ + ...cluster, + trend: trends.map(({ timestamp, clusters: clustersTrendData }) => ({ + timestamp, + ...clustersTrendData[cluster.meta.clusterId], + })), + })); + +const getSummaryTrend = (trends: Trends) => + trends.map(({ timestamp, summary }) => ({ timestamp, ...summary })); + // TODO: Utilize ES "Point in Time" feature https://www.elastic.co/guide/en/elasticsearch/reference/current/point-in-time-api.html export const defineGetComplianceDashboardRoute = ( router: CspRouter, @@ -96,16 +109,21 @@ export const defineGetComplianceDashboardRoute = ( }, }; - const [stats, resourcesTypes, clusters] = await Promise.all([ + const [stats, resourcesTypes, clustersWithoutTrends, trends] = await Promise.all([ getStats(esClient, query), getResourcesTypes(esClient, query), getClusters(esClient, query), + getTrends(esClient), ]); + const clusters = getClustersTrends(clustersWithoutTrends, trends); + const trend = getSummaryTrend(trends); + const body: ComplianceDashboardData = { stats, resourcesTypes, clusters, + trend, }; return response.ok({ diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts index 98171bf5ec332..a030a744c2d2d 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts @@ -11,7 +11,7 @@ import type { QueryDslQueryContainer, SearchRequest, } from '@elastic/elasticsearch/lib/api/types'; -import { ComplianceDashboardData } from '../../../common/types'; +import { Cluster } from '../../../common/types'; import { getResourceTypeFromAggs, resourceTypeAggQuery } from './get_resources_types'; import type { ResourceTypeQueryResult } from './get_resources_types'; import { CSP_KUBEBEAT_INDEX_PATTERN } from '../../../common/constants'; @@ -35,6 +35,8 @@ interface ClustersQueryResult { aggs_by_cluster_id: Aggregation; } +export type ClusterWithoutTrend = Omit; + export const getClustersQuery = (query: QueryDslQueryContainer): SearchRequest => ({ index: CSP_KUBEBEAT_INDEX_PATTERN, size: 0, @@ -66,9 +68,7 @@ export const getClustersQuery = (query: QueryDslQueryContainer): SearchRequest = }, }); -export const getClustersFromAggs = ( - clusters: ClusterBucket[] -): ComplianceDashboardData['clusters'] => +export const getClustersFromAggs = (clusters: ClusterBucket[]): ClusterWithoutTrend[] => clusters.map((cluster) => { // get cluster's meta data const benchmarks = cluster.benchmarks.buckets; @@ -103,7 +103,7 @@ export const getClustersFromAggs = ( export const getClusters = async ( esClient: ElasticsearchClient, query: QueryDslQueryContainer -): Promise => { +): Promise => { const queryResult = await esClient.search(getClustersQuery(query), { meta: true, }); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_trends.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_trends.test.ts new file mode 100644 index 0000000000000..127cf3e1a3f80 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_trends.test.ts @@ -0,0 +1,131 @@ +/* + * 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 { getTrendsFromQueryResult, ScoreTrendDoc } from './get_trends'; + +const trendDocs: ScoreTrendDoc[] = [ + { + '@timestamp': '2022-04-06T15:30:00Z', + total_findings: 20, + passed_findings: 5, + failed_findings: 15, + score_by_cluster_id: { + first_cluster_id: { + total_findings: 20, + passed_findings: 5, + failed_findings: 15, + }, + }, + }, + { + '@timestamp': '2022-04-06T15:00:00Z', + total_findings: 40, + passed_findings: 25, + failed_findings: 15, + score_by_cluster_id: { + second_cluster_id: { + total_findings: 20, + passed_findings: 10, + failed_findings: 10, + }, + third_cluster_id: { + total_findings: 20, + passed_findings: 15, + failed_findings: 5, + }, + }, + }, + { + '@timestamp': '2022-04-05T15:30:00Z', + total_findings: 30, + passed_findings: 25, + failed_findings: 5, + score_by_cluster_id: { + forth_cluster_id: { + total_findings: 25, + passed_findings: 25, + failed_findings: 0, + }, + fifth_cluster_id: { + total_findings: 5, + passed_findings: 0, + failed_findings: 5, + }, + }, + }, +]; + +describe('getTrendsFromQueryResult', () => { + it('should return value matching Trends type definition, in descending order, and with postureScore', async () => { + const trends = getTrendsFromQueryResult(trendDocs); + expect(trends).toEqual([ + { + timestamp: '2022-04-06T15:30:00Z', + summary: { + totalFindings: 20, + totalPassed: 5, + totalFailed: 15, + postureScore: 25.0, + }, + clusters: { + first_cluster_id: { + totalFindings: 20, + totalPassed: 5, + totalFailed: 15, + postureScore: 25.0, + }, + }, + }, + { + timestamp: '2022-04-06T15:00:00Z', + summary: { + totalFindings: 40, + totalPassed: 25, + totalFailed: 15, + postureScore: 62.5, + }, + clusters: { + second_cluster_id: { + totalFindings: 20, + totalPassed: 10, + totalFailed: 10, + postureScore: 50.0, + }, + third_cluster_id: { + totalFindings: 20, + totalPassed: 15, + totalFailed: 5, + postureScore: 75.0, + }, + }, + }, + { + timestamp: '2022-04-05T15:30:00Z', + summary: { + totalFindings: 30, + totalPassed: 25, + totalFailed: 5, + postureScore: 83.3, + }, + clusters: { + forth_cluster_id: { + totalFindings: 25, + totalPassed: 25, + totalFailed: 0, + postureScore: 100.0, + }, + fifth_cluster_id: { + totalFindings: 5, + totalPassed: 0, + totalFailed: 5, + postureScore: 0, + }, + }, + }, + ]); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_trends.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_trends.ts new file mode 100644 index 0000000000000..a432d275fdb64 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_trends.ts @@ -0,0 +1,73 @@ +/* + * 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 { ElasticsearchClient } from 'kibana/server'; +import { BENCHMARK_SCORE_INDEX_PATTERN } from '../../../common/constants'; +import { Stats } from '../../../common/types'; +import { calculatePostureScore } from './get_stats'; + +export interface ScoreTrendDoc { + '@timestamp': string; + total_findings: number; + passed_findings: number; + failed_findings: number; + score_by_cluster_id: Record< + string, + { + total_findings: number; + passed_findings: number; + failed_findings: number; + } + >; +} + +export const getTrendsAggsQuery = () => ({ + index: BENCHMARK_SCORE_INDEX_PATTERN, + size: 5, + sort: '@timestamp:desc', +}); + +export type Trends = Array<{ + timestamp: string; + summary: Stats; + clusters: Record; +}>; + +export const getTrendsFromQueryResult = (scoreTrendDocs: ScoreTrendDoc[]): Trends => + scoreTrendDocs.map((data) => ({ + timestamp: data['@timestamp'], + summary: { + totalFindings: data.total_findings, + totalFailed: data.failed_findings, + totalPassed: data.passed_findings, + postureScore: calculatePostureScore(data.passed_findings, data.failed_findings), + }, + clusters: Object.fromEntries( + Object.entries(data.score_by_cluster_id).map(([clusterId, cluster]) => [ + clusterId, + { + totalFindings: cluster.total_findings, + totalFailed: cluster.failed_findings, + totalPassed: cluster.passed_findings, + postureScore: calculatePostureScore(cluster.passed_findings, cluster.failed_findings), + }, + ]) + ), + })); + +export const getTrends = async (esClient: ElasticsearchClient): Promise => { + const trendsQueryResult = await esClient.search(getTrendsAggsQuery()); + + if (!trendsQueryResult.hits.hits) throw new Error('missing trend results from score index'); + + const scoreTrendDocs = trendsQueryResult.hits.hits.map((hit) => { + if (!hit._source) throw new Error('missing _source data for one or more of trend results'); + return hit._source; + }); + + return getTrendsFromQueryResult(scoreTrendDocs); +}; From 70cf1d32e6dc6286ee75a785e4c2761b3930a5af Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Mon, 11 Apr 2022 12:46:13 +0200 Subject: [PATCH 26/42] Fix Host by Risk global deep link search not present (#129869) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/app/deep_links/index.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/security_solution/public/app/deep_links/index.ts b/x-pack/plugins/security_solution/public/app/deep_links/index.ts index a6eb2f04a6c75..c14a6d04273d3 100644 --- a/x-pack/plugins/security_solution/public/app/deep_links/index.ts +++ b/x-pack/plugins/security_solution/public/app/deep_links/index.ts @@ -207,6 +207,14 @@ export const securitySolutionsDeepLinks: SecuritySolutionDeepLink[] = [ }), path: `${HOSTS_PATH}/uncommonProcesses`, }, + { + id: SecurityPageName.hostsAnomalies, + title: i18n.translate('xpack.securitySolution.search.hosts.anomalies', { + defaultMessage: 'Anomalies', + }), + path: `${HOSTS_PATH}/anomalies`, + isPremium: true, + }, { id: SecurityPageName.hostsEvents, title: i18n.translate('xpack.securitySolution.search.hosts.events', { @@ -222,12 +230,12 @@ export const securitySolutionsDeepLinks: SecuritySolutionDeepLink[] = [ path: `${HOSTS_PATH}/externalAlerts`, }, { - id: SecurityPageName.hostsAnomalies, - title: i18n.translate('xpack.securitySolution.search.hosts.anomalies', { - defaultMessage: 'Anomalies', + id: SecurityPageName.usersRisk, + title: i18n.translate('xpack.securitySolution.search.hosts.risk', { + defaultMessage: 'Hosts by risk', }), - path: `${HOSTS_PATH}/anomalies`, - isPremium: true, + path: `${HOSTS_PATH}/hostRisk`, + experimentalKey: 'riskyHostsEnabled', }, { id: SecurityPageName.sessions, From a785f0c3d5a84b503b81683a0b8223bf91988f47 Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Mon, 11 Apr 2022 12:46:38 +0200 Subject: [PATCH 27/42] Fix Name mismatch for User by risk tabs under global search (#129759) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/security_solution/public/app/deep_links/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/app/deep_links/index.ts b/x-pack/plugins/security_solution/public/app/deep_links/index.ts index c14a6d04273d3..c80f2ca282f47 100644 --- a/x-pack/plugins/security_solution/public/app/deep_links/index.ts +++ b/x-pack/plugins/security_solution/public/app/deep_links/index.ts @@ -327,7 +327,7 @@ export const securitySolutionsDeepLinks: SecuritySolutionDeepLink[] = [ { id: SecurityPageName.usersRisk, title: i18n.translate('xpack.securitySolution.search.users.risk', { - defaultMessage: 'Risk', + defaultMessage: 'Users by risk', }), path: `${USERS_PATH}/userRisk`, experimentalKey: 'riskyUsersEnabled', From f3145a8b76265ec4f30f0dd3fa8422f368fd3ba7 Mon Sep 17 00:00:00 2001 From: "Lucas F. da Costa" Date: Mon, 11 Apr 2022 11:51:19 +0100 Subject: [PATCH 28/42] [Uptime] Add migration to include synthetics and heartbeat indices on 8.2.0 (#129510) * [Uptime] add migration to include synthetics and heartbeat indices on 8.2.0 * [uptime] add stricter typing to 8.2.0 indices migration --- .../lib/saved_objects/migrations.test.ts | 93 +++++++++++++++++++ .../server/lib/saved_objects/migrations.ts | 33 +++++++ .../lib/saved_objects/uptime_settings.ts | 6 ++ 3 files changed, 132 insertions(+) create mode 100644 x-pack/plugins/uptime/server/lib/saved_objects/migrations.test.ts create mode 100644 x-pack/plugins/uptime/server/lib/saved_objects/migrations.ts diff --git a/x-pack/plugins/uptime/server/lib/saved_objects/migrations.test.ts b/x-pack/plugins/uptime/server/lib/saved_objects/migrations.test.ts new file mode 100644 index 0000000000000..172e549cc05fa --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/saved_objects/migrations.test.ts @@ -0,0 +1,93 @@ +/* + * 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 { add820Indices } from './migrations'; +import { SavedObject, SavedObjectMigrationContext } from 'src/core/server'; +import { DynamicSettings } from '../../../common/runtime_types'; + +describe('add820Indices migration', () => { + const context = { log: { warning: () => {} } } as unknown as SavedObjectMigrationContext; + + const makeSettings = (heartbeatIndices: string): SavedObject => { + return { + id: '1', + type: 't', + references: [], + attributes: { + heartbeatIndices, + certAgeThreshold: 1, + certExpirationThreshold: 2, + defaultConnectors: ['example'], + }, + }; + }; + + it("adds the synthetics-* index if it's not in the indices settings", () => { + const doc = makeSettings('heartbeat-8*,something_else'); + const result = add820Indices(doc, context); + expect(result).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + heartbeatIndices: 'heartbeat-8*,something_else,synthetics-*', + }, + }); + }); + + it("adds the heartbeat-8* index if it's not in the indices settings", () => { + const doc = makeSettings('synthetics-*,something_else'); + const result = add820Indices(doc, context); + expect(result).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + heartbeatIndices: 'synthetics-*,something_else,heartbeat-8*', + }, + }); + }); + + it("adds both synthetics-* and heartbeat-8* index if they're not present in the indices", () => { + const doc = makeSettings('something-*,something_else'); + const result = add820Indices(doc, context); + expect(result).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + heartbeatIndices: 'something-*,something_else,synthetics-*,heartbeat-8*', + }, + }); + }); + + it('works for empty heartbeat indices fields', () => { + const doc = makeSettings(''); + const result = add820Indices(doc, context); + expect(result).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + heartbeatIndices: 'synthetics-*,heartbeat-8*', + }, + }); + }); + + it('works for undefined heartbeat indices fields', () => { + const doc = makeSettings(''); + + // We must TS ignore this so that we can delete this + // non-optional field and test that the migration still works + // when the field does not exist in the document + // @ts-expect-error + delete doc.attributes.heartbeatIndices; + const result = add820Indices(doc, context); + expect(result).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + heartbeatIndices: 'synthetics-*,heartbeat-8*', + }, + }); + }); +}); diff --git a/x-pack/plugins/uptime/server/lib/saved_objects/migrations.ts b/x-pack/plugins/uptime/server/lib/saved_objects/migrations.ts new file mode 100644 index 0000000000000..211e58db9784e --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/saved_objects/migrations.ts @@ -0,0 +1,33 @@ +/* + * 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 { SavedObjectMigrationFn } from 'src/core/server'; +import { DynamicSettings } from '../../../common/runtime_types'; + +export const add820Indices: SavedObjectMigrationFn = (doc) => { + const heartbeatIndices = doc.attributes?.heartbeatIndices; + + const indicesArr = !heartbeatIndices ? [] : heartbeatIndices.split(','); + + if (!indicesArr.includes('synthetics-*')) { + indicesArr.push('synthetics-*'); + } + + if (!indicesArr.includes('heartbeat-8*')) { + indicesArr.push('heartbeat-8*'); + } + + const migratedObj = { + ...doc, + attributes: { + ...doc.attributes, + heartbeatIndices: indicesArr.join(','), + }, + }; + + return migratedObj; +}; diff --git a/x-pack/plugins/uptime/server/lib/saved_objects/uptime_settings.ts b/x-pack/plugins/uptime/server/lib/saved_objects/uptime_settings.ts index 0360b0ed11eb2..4ed7887049c9b 100644 --- a/x-pack/plugins/uptime/server/lib/saved_objects/uptime_settings.ts +++ b/x-pack/plugins/uptime/server/lib/saved_objects/uptime_settings.ts @@ -7,6 +7,8 @@ import { SavedObjectsType } from 'kibana/server'; import { i18n } from '@kbn/i18n'; +import { add820Indices } from './migrations'; + export const settingsObjectType = 'uptime-dynamic-settings'; export const settingsObjectId = 'uptime-dynamic-settings-singleton'; @@ -43,4 +45,8 @@ export const umDynamicSettings: SavedObjectsType = { defaultMessage: 'Uptime Settings - Index', }), }, + migrations: { + // Takes a pre 8.2.0 doc, and converts it to 8.2.0 + '8.2.0': add820Indices, + }, }; From a706a308a0b538868463f68c978a4f5772a1f6a6 Mon Sep 17 00:00:00 2001 From: Dmitry Tomashevich <39378793+Dmitriynj@users.noreply.github.com> Date: Mon, 11 Apr 2022 16:16:09 +0500 Subject: [PATCH 29/42] [Discover] Fix `Filter for field present` in expanded document view of Document Explorer (#129588) * [Discover] fix filter field present * [Discover] improve functional tests * [Discover] fix functional tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../doc_viewer_table/table_cell_actions.tsx | 3 +- ...on.ts => _classic_table_doc_navigation.ts} | 41 +++++++------------ .../discover/_data_grid_doc_navigation.ts | 33 +++------------ test/functional/apps/discover/index.ts | 2 +- 4 files changed, 23 insertions(+), 56 deletions(-) rename test/functional/apps/discover/{_doc_navigation.ts => _classic_table_doc_navigation.ts} (66%) diff --git a/src/plugins/discover/public/services/doc_views/components/doc_viewer_table/table_cell_actions.tsx b/src/plugins/discover/public/services/doc_views/components/doc_viewer_table/table_cell_actions.tsx index 9a50811ff0d8c..d5dc166e639f3 100644 --- a/src/plugins/discover/public/services/doc_views/components/doc_viewer_table/table_cell_actions.tsx +++ b/src/plugins/discover/public/services/doc_views/components/doc_viewer_table/table_cell_actions.tsx @@ -147,7 +147,8 @@ export const TableActions = ({ toolTipContent: filtersExistsToolTip, icon: 'filter', disabled: filtersExistsDisabled, - onClick: onClickAction(onFilter.bind({}, fieldMapping, flattenedField, '-')), + 'data-test-subj': `addExistsFilterButton-${field}`, + onClick: onClickAction(onFilter.bind({}, '_exists_', field, '+')), }, { name: toggleColumnsLabel, diff --git a/test/functional/apps/discover/_doc_navigation.ts b/test/functional/apps/discover/_classic_table_doc_navigation.ts similarity index 66% rename from test/functional/apps/discover/_doc_navigation.ts rename to test/functional/apps/discover/_classic_table_doc_navigation.ts index 19f61851ef961..c768d9600c189 100644 --- a/test/functional/apps/discover/_doc_navigation.ts +++ b/test/functional/apps/discover/_classic_table_doc_navigation.ts @@ -19,12 +19,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const kibanaServer = getService('kibanaServer'); - describe('doc link in discover', function contextSize() { + describe('classic table doc link', function contextSize() { before(async () => { await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); await kibanaServer.uiSettings.update({ + defaultIndex: 'logstash-*', 'doc_table:legacy': true, 'discover:searchFieldsFromSource': true, }); @@ -39,6 +40,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.waitForDocTableLoadingComplete(); }); + beforeEach(async function () { + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await PageObjects.common.navigateToApp('discover'); + await PageObjects.discover.waitForDocTableLoadingComplete(); + }); + it('should open the doc view of the selected document', async function () { // navigate to the doc view await docTable.clickRowToggle({ rowIndex: 0 }); @@ -58,35 +65,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - // no longer relevant as null field won't be returned in the Fields API response - xit('add filter should create an exists filter if value is null (#7189)', async function () { + it('should create an exists filter from the doc view of the selected document', async function () { await PageObjects.discover.waitUntilSearchingHasFinished(); - // Filter special document - await filterBar.addFilter('agent', 'is', 'Missing/Fields'); - await PageObjects.discover.waitUntilSearchingHasFinished(); - - await retry.try(async () => { - // navigate to the doc view - await docTable.clickRowToggle({ rowIndex: 0 }); - const details = await docTable.getDetailsRow(); - await docTable.addInclusiveFilter(details, 'referer'); - await PageObjects.discover.waitUntilSearchingHasFinished(); + await docTable.toggleRowExpanded(); + const detailsRow = await docTable.getDetailsRow(); + await docTable.addExistsFilter(detailsRow, '@timestamp'); - const hasInclusiveFilter = await filterBar.hasFilter( - 'referer', - 'exists', - true, - false, - true - ); - expect(hasInclusiveFilter).to.be(true); - - await docTable.removeInclusiveFilter(details, 'referer'); - await PageObjects.discover.waitUntilSearchingHasFinished(); - const hasExcludeFilter = await filterBar.hasFilter('referer', 'exists', true, false, false); - expect(hasExcludeFilter).to.be(true); - }); + const hasExistsFilter = await filterBar.hasFilter('@timestamp', 'exists', true, false, false); + expect(hasExistsFilter).to.be(true); }); }); } diff --git a/test/functional/apps/discover/_data_grid_doc_navigation.ts b/test/functional/apps/discover/_data_grid_doc_navigation.ts index 73077dcc9749a..2da6db97aa13f 100644 --- a/test/functional/apps/discover/_data_grid_doc_navigation.ts +++ b/test/functional/apps/discover/_data_grid_doc_navigation.ts @@ -54,37 +54,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - // no longer relevant as null field won't be returned in the Fields API response - xit('add filter should create an exists filter if value is null (#7189)', async function () { + it('should create an exists filter from doc view of the selected document', async function () { await PageObjects.discover.waitUntilSearchingHasFinished(); - // Filter special document - await filterBar.addFilter('agent', 'is', 'Missing/Fields'); - await PageObjects.discover.waitUntilSearchingHasFinished(); - - await retry.try(async () => { - // navigate to the doc view - await dataGrid.clickRowToggle({ rowIndex: 0 }); - const details = await dataGrid.getDetailsRow(); - await dataGrid.addInclusiveFilter(details, 'referer'); - await PageObjects.discover.waitUntilSearchingHasFinished(); + await dataGrid.clickRowToggle({ rowIndex: 0 }); - const hasInclusiveFilter = await filterBar.hasFilter( - 'referer', - 'exists', - true, - false, - true - ); - expect(hasInclusiveFilter).to.be(true); + await testSubjects.click('openFieldActionsButton-@timestamp'); + await testSubjects.click('addExistsFilterButton-@timestamp'); - await dataGrid.clickRowToggle({ rowIndex: 0 }); - const detailsExcluding = await dataGrid.getDetailsRow(); - await dataGrid.removeInclusiveFilter(detailsExcluding, 'referer'); - await PageObjects.discover.waitUntilSearchingHasFinished(); - const hasExcludeFilter = await filterBar.hasFilter('referer', 'exists', true, false, false); - expect(hasExcludeFilter).to.be(true); - }); + const hasExistsFilter = await filterBar.hasFilter('@timestamp', 'exists', true, false, false); + expect(hasExistsFilter).to.be(true); }); }); } diff --git a/test/functional/apps/discover/index.ts b/test/functional/apps/discover/index.ts index c9497e872d7b9..994cfeb851281 100644 --- a/test/functional/apps/discover/index.ts +++ b/test/functional/apps/discover/index.ts @@ -37,7 +37,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_source_filters')); loadTestFile(require.resolve('./_large_string')); loadTestFile(require.resolve('./_inspector')); - loadTestFile(require.resolve('./_doc_navigation')); + loadTestFile(require.resolve('./_classic_table_doc_navigation')); loadTestFile(require.resolve('./_date_nanos')); loadTestFile(require.resolve('./_date_nanos_mixed')); loadTestFile(require.resolve('./_indexpattern_without_timefield')); From b82227588485c543909047d1c3553a747629c835 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Mon, 11 Apr 2022 13:43:01 +0200 Subject: [PATCH 30/42] es.createClient: improve config merging logic (#129587) * es.createClient: improve config merging logic * add additional test * fix test * address review comments Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../client/client_config.test.ts | 6 +- .../elasticsearch/client/client_config.ts | 5 +- .../elasticsearch/elasticsearch_service.ts | 8 +- .../server/elasticsearch/merge_config.test.ts | 136 ++++++++++++++++++ src/core/server/elasticsearch/merge_config.ts | 31 ++++ 5 files changed, 177 insertions(+), 9 deletions(-) create mode 100644 src/core/server/elasticsearch/merge_config.test.ts create mode 100644 src/core/server/elasticsearch/merge_config.ts diff --git a/src/core/server/elasticsearch/client/client_config.test.ts b/src/core/server/elasticsearch/client/client_config.test.ts index fb5f83c2a6bc0..dcc6d25c565bf 100644 --- a/src/core/server/elasticsearch/client/client_config.test.ts +++ b/src/core/server/elasticsearch/client/client_config.test.ts @@ -269,9 +269,9 @@ describe('parseClientOptions', () => { ) ).toEqual( expect.objectContaining({ - headers: expect.objectContaining({ - authorization: `Bearer ABC123`, - }), + auth: { + bearer: `ABC123`, + }, }) ); }); diff --git a/src/core/server/elasticsearch/client/client_config.ts b/src/core/server/elasticsearch/client/client_config.ts index 9a0b72a36c3db..63b9555a420ab 100644 --- a/src/core/server/elasticsearch/client/client_config.ts +++ b/src/core/server/elasticsearch/client/client_config.ts @@ -88,8 +88,9 @@ export function parseClientOptions( password: config.password, }; } else if (config.serviceAccountToken) { - // TODO: change once ES client has native support for service account tokens: https://github.com/elastic/elasticsearch-js/issues/1477 - clientOptions.headers!.authorization = `Bearer ${config.serviceAccountToken}`; + clientOptions.auth = { + bearer: config.serviceAccountToken, + }; } } diff --git a/src/core/server/elasticsearch/elasticsearch_service.ts b/src/core/server/elasticsearch/elasticsearch_service.ts index 4ab59d12942e7..07462a52ef220 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.ts @@ -8,7 +8,6 @@ import { Observable, Subject } from 'rxjs'; import { first, map, shareReplay, takeUntil } from 'rxjs/operators'; -import { merge } from '@kbn/std'; import { CoreService } from '../../types'; import { CoreContext } from '../core_context'; @@ -29,6 +28,7 @@ import { calculateStatus$ } from './status'; import { isValidConnection } from './is_valid_connection'; import { isInlineScriptingEnabled } from './is_scripting_enabled'; import type { UnauthorizedErrorHandler } from './client/retry_unauthorized'; +import { mergeConfig } from './merge_config'; export interface SetupDeps { http: InternalHttpServiceSetup; @@ -154,10 +154,10 @@ export class ElasticsearchService private createClusterClient( type: string, - baseConfig: ElasticsearchConfig, - clientConfig?: Partial + baseConfig: ElasticsearchClientConfig, + clientConfig: Partial = {} ) { - const config = clientConfig ? merge({}, baseConfig, clientConfig) : baseConfig; + const config = mergeConfig(baseConfig, clientConfig); return new ClusterClient({ config, logger: this.coreContext.logger.get('elasticsearch'), diff --git a/src/core/server/elasticsearch/merge_config.test.ts b/src/core/server/elasticsearch/merge_config.test.ts new file mode 100644 index 0000000000000..c89a759435284 --- /dev/null +++ b/src/core/server/elasticsearch/merge_config.test.ts @@ -0,0 +1,136 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { mergeConfig } from './merge_config'; +import type { ElasticsearchClientConfig } from './client'; +import { configSchema, ElasticsearchConfig } from './elasticsearch_config'; + +const partialToConfig = (parts: Partial): ElasticsearchClientConfig => { + return parts as ElasticsearchClientConfig; +}; + +describe('mergeConfig', () => { + it('merges the base config and the overrides', () => { + const base = partialToConfig({ + hosts: ['localhost'], + compression: true, + }); + const overrides: Partial = { + maxSockets: 42, + }; + + expect(mergeConfig(base, overrides)).toEqual({ + hosts: ['localhost'], + compression: true, + maxSockets: 42, + }); + }); + + it('properly override values that are present in both the base config and the overrides', () => { + const base = partialToConfig({ + hosts: ['localhost'], + compression: true, + }); + const overrides: Partial = { + compression: false, + maxSockets: 42, + }; + + expect(mergeConfig(base, overrides)).toEqual({ + hosts: ['localhost'], + compression: false, + maxSockets: 42, + }); + }); + + it('fully override arrays instead of aggregating them', () => { + const base = partialToConfig({ + hosts: ['localhost'], + }); + const overrides: Partial = { + hosts: ['another-host'], + }; + + expect(mergeConfig(base, overrides)).toEqual({ + hosts: ['another-host'], + }); + }); + + it('ignores the `username` and `password` fields from the base config if overrides defines `serviceAccountToken`', () => { + const base = partialToConfig({ + hosts: ['localhost'], + username: 'foo', + password: 'bar', + }); + const overrides: Partial = { + hosts: ['another-host'], + serviceAccountToken: 'token', + }; + + expect(mergeConfig(base, overrides)).toEqual({ + hosts: ['another-host'], + serviceAccountToken: 'token', + }); + }); + + it('ignores the `serviceAccountToken` field from the base config if overrides defines `username` and `password`', () => { + const base = partialToConfig({ + hosts: ['localhost'], + serviceAccountToken: 'token', + }); + const overrides: Partial = { + hosts: ['another-host'], + username: 'foo', + password: 'bar', + }; + + expect(mergeConfig(base, overrides)).toEqual({ + hosts: ['another-host'], + username: 'foo', + password: 'bar', + }); + }); + + it('does not mutate the base config', () => { + const base = partialToConfig({ + hosts: ['localhost'], + maxSockets: 42, + }); + const overrides: Partial = { + hosts: ['another-host'], + maxSockets: 9000, + compression: true, + }; + + mergeConfig(base, overrides); + + expect(base).toEqual({ + hosts: ['localhost'], + maxSockets: 42, + }); + }); + + it('works with a real instance of ElasticsearchConfig', () => { + const rawConfig = configSchema.validate({ + username: 'foo', + password: 'bar', + }); + + const baseConfig = new ElasticsearchConfig(rawConfig); + + const overrides: Partial = { + serviceAccountToken: 'token', + }; + + const output = mergeConfig(baseConfig, overrides); + + expect(output.serviceAccountToken).toEqual('token'); + expect(output.username).toBeUndefined(); + expect(output.password).toBeUndefined(); + }); +}); diff --git a/src/core/server/elasticsearch/merge_config.ts b/src/core/server/elasticsearch/merge_config.ts new file mode 100644 index 0000000000000..373bf6fc6cbe0 --- /dev/null +++ b/src/core/server/elasticsearch/merge_config.ts @@ -0,0 +1,31 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { merge } from '@kbn/std'; +import { Writable } from '@kbn/utility-types'; +import type { ElasticsearchClientConfig } from './client'; + +type WritableConfig = Writable; + +export const mergeConfig = ( + baseConfig: ElasticsearchClientConfig, + configOverrides: Partial +): ElasticsearchClientConfig => { + // note: spreading the source is sufficient because we're only removing keys + // if we were to perform more complex operations, a deep copy would be required here + const writableBaseConfig = { ...baseConfig } as WritableConfig; + + if (configOverrides.serviceAccountToken) { + delete writableBaseConfig.username; + delete writableBaseConfig.password; + } else if (configOverrides.username && configOverrides.password) { + delete writableBaseConfig.serviceAccountToken; + } + + return merge(writableBaseConfig, configOverrides); +}; From b5dae3862e650dc1706570b1e52db51988f47504 Mon Sep 17 00:00:00 2001 From: Thom Heymann <190132+thomheymann@users.noreply.github.com> Date: Mon, 11 Apr 2022 13:02:15 +0100 Subject: [PATCH 31/42] Fix styles for "You do not have permission" screen (#129715) * Fix styles for "You do not have permission" screen * . Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../authorization/authorization_service.tsx | 4 +-- .../plugins/security/server/prompt_page.tsx | 33 +++++++++---------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/security/server/authorization/authorization_service.tsx b/x-pack/plugins/security/server/authorization/authorization_service.tsx index 004c82d2c3f3f..96bb1c6289b4a 100644 --- a/x-pack/plugins/security/server/authorization/authorization_service.tsx +++ b/x-pack/plugins/security/server/authorization/authorization_service.tsx @@ -7,7 +7,7 @@ import querystring from 'querystring'; import React from 'react'; -import { renderToStaticMarkup } from 'react-dom/server'; +import { renderToString } from 'react-dom/server'; import type { Observable, Subscription } from 'rxjs'; import type { @@ -178,7 +178,7 @@ export class AuthorizationService { http.registerOnPreResponse((request, preResponse, toolkit) => { if (preResponse.statusCode === 403 && canRedirectRequest(request)) { const next = `${http.basePath.get(request)}${request.url.pathname}${request.url.search}`; - const body = renderToStaticMarkup( + const body = renderToString( - - - - {title}} - body={body} - actions={actions} - /> - - - + + + + + {title}} + body={body} + actions={actions} + /> + + + + From 6542dc194af61b89f97e24e974daa37476449ecb Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Mon, 11 Apr 2022 07:08:21 -0500 Subject: [PATCH 32/42] [Discover] Better a11y for page header (#129789) --- .../main/components/layout/discover_layout.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index f8ea65a58b3a9..b6ee4133f1f75 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -234,7 +234,16 @@ export function DiscoverLayout({ history={history} />

- {savedSearch.title} + {savedSearch.title + ? i18n.translate('discover.pageTitleWithSavedSearch', { + defaultMessage: 'Discover - {savedSearchTitle}', + values: { + savedSearchTitle: savedSearch.title, + }, + }) + : i18n.translate('discover.pageTitleWithoutSavedSearch', { + defaultMessage: 'Discover - Search not yet saved', + })}

From ae39e1bae70e26a490f2344bc78dfdc85843c593 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Mon, 11 Apr 2022 07:13:45 -0500 Subject: [PATCH 33/42] Removing axios from data_view_field_editor test (#129116) * test bed fixes Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../client_integration/field_editor.test.tsx | 3 +- .../field_editor_flyout_content.test.ts | 3 +- .../field_editor_flyout_preview.test.ts | 27 +++--------- .../helpers/http_requests.ts | 43 ++++++++----------- .../helpers/setup_environment.tsx | 10 ++--- 5 files changed, 30 insertions(+), 56 deletions(-) diff --git a/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor.test.tsx b/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor.test.tsx index 55b9876ac54ad..1ba108492e7f9 100644 --- a/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor.test.tsx +++ b/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor.test.tsx @@ -19,7 +19,7 @@ import { } from './field_editor.helpers'; describe('', () => { - const { server, httpRequestsMockHelpers } = setupEnvironment(); + const { httpRequestsMockHelpers } = setupEnvironment(); let testBed: FieldEditorTestBed; let onChange: jest.Mock = jest.fn(); @@ -70,7 +70,6 @@ describe('', () => { afterAll(() => { jest.useRealTimers(); - server.restore(); }); beforeEach(async () => { diff --git a/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_content.test.ts b/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_content.test.ts index 1730593dbda20..a3ae323a1c3db 100644 --- a/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_content.test.ts +++ b/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_content.test.ts @@ -15,7 +15,7 @@ import { setup } from './field_editor_flyout_content.helpers'; import { mockDocuments, createPreviewError } from './helpers/mocks'; describe('', () => { - const { server, httpRequestsMockHelpers } = setupEnvironment(); + const { httpRequestsMockHelpers } = setupEnvironment(); beforeAll(() => { jest.useFakeTimers(); @@ -23,7 +23,6 @@ describe('', () => { afterAll(() => { jest.useRealTimers(); - server.restore(); }); beforeEach(async () => { diff --git a/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts b/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts index c3175d7097a36..fc680213c1995 100644 --- a/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts +++ b/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts @@ -32,7 +32,6 @@ describe('Field editor Preview panel', () => { afterAll(() => { jest.useRealTimers(); - server.restore(); }); let testBed: FieldEditorFlyoutContentTestBed; @@ -55,9 +54,6 @@ describe('Field editor Preview panel', () => { ]; beforeEach(async () => { - server.respondImmediately = true; - server.autoRespond = true; - httpRequestsMockHelpers.setFieldPreviewResponse({ values: ['mockedScriptValue'] }); setIndexPatternFields(indexPatternFields); setSearchResponse(mockDocuments); @@ -278,23 +274,18 @@ describe('Field editor Preview panel', () => { httpRequestsMockHelpers.setFieldPreviewResponse({ values: [scriptEmitResponse] }); const { - actions: { - toggleFormRow, - fields, - waitForUpdates, - getLatestPreviewHttpRequest, - getRenderedFieldsPreview, - }, + actions: { toggleFormRow, fields, waitForUpdates, getRenderedFieldsPreview }, } = testBed; await toggleFormRow('value'); await fields.updateName('myRuntimeField'); await fields.updateScript('echo("hello")'); await waitForUpdates(); // Run validations - const request = getLatestPreviewHttpRequest(server); // Make sure the payload sent is correct - expect(request.requestBody).toEqual({ + const firstCall = server.post.mock.calls[0] as Array<{ body: any }>; + const payload = JSON.parse(firstCall[1]?.body); + expect(payload).toEqual({ context: 'keyword_field', document: { description: 'First doc - description', @@ -373,10 +364,8 @@ describe('Field editor Preview panel', () => { test('should display an updating indicator while fetching the docs and the preview', async () => { // We want to test if the loading indicator is in the DOM, for that we don't want the server to // respond immediately. We'll manualy send the response. - server.respondImmediately = false; - server.autoRespond = false; - httpRequestsMockHelpers.setFieldPreviewResponse({ values: ['ok'] }); + httpRequestsMockHelpers.setFieldPreviewResponse({ values: ['ok'] }, undefined, true); const { exists, @@ -394,17 +383,14 @@ describe('Field editor Preview panel', () => { await fields.updateScript('echo("hello")'); expect(exists('isUpdatingIndicator')).toBe(true); // indicator while getting preview - server.respond(); await waitForUpdates(); expect(exists('isUpdatingIndicator')).toBe(false); }); test('should not display the updating indicator when neither the type nor the script has changed', async () => { - httpRequestsMockHelpers.setFieldPreviewResponse({ values: ['ok'] }); + httpRequestsMockHelpers.setFieldPreviewResponse({ values: ['ok'] }, undefined, true); // We want to test if the loading indicator is in the DOM, for that we need to manually // send the response from the server - server.respondImmediately = false; - server.autoRespond = false; const { exists, @@ -417,7 +403,6 @@ describe('Field editor Preview panel', () => { await fields.updateScript('echo("hello")'); expect(exists('isUpdatingIndicator')).toBe(true); - server.respond(); await waitForDocumentsAndPreviewUpdate(); expect(exists('isUpdatingIndicator')).toBe(false); diff --git a/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/http_requests.ts b/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/http_requests.ts index 4b03db247bad1..4c7a5832f037c 100644 --- a/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/http_requests.ts +++ b/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/http_requests.ts @@ -6,42 +6,37 @@ * Side Public License, v 1. */ -import sinon, { SinonFakeServer } from 'sinon'; -import { API_BASE_PATH } from '../../../common/constants'; +import { httpServiceMock } from '../../../../../../src/core/public/mocks'; type HttpResponse = Record | any[]; -// Register helpers to mock HTTP Requests -const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { - const setFieldPreviewResponse = (response?: HttpResponse, error?: any) => { - const status = error ? error.body.status || 400 : 200; - const body = error ? JSON.stringify(error.body) : JSON.stringify(response); - - server.respondWith('POST', `${API_BASE_PATH}/field_preview`, [ - status, - { 'Content-Type': 'application/json' }, - body, - ]); +const registerHttpRequestMockHelpers = ( + httpSetup: ReturnType +) => { + const setFieldPreviewResponse = (response?: HttpResponse, error?: any, delayResponse = false) => { + const body = error ? JSON.stringify(error.body) : response; + + httpSetup.post.mockImplementation(() => { + if (delayResponse) { + return new Promise((resolve) => { + setTimeout(() => resolve({ data: body }), 1000); + }); + } else { + return Promise.resolve({ data: body }); + } + }); }; - return { setFieldPreviewResponse, }; }; export const init = () => { - const server = sinon.fakeServer.create(); - server.respondImmediately = true; - - // Define default response for unhandled requests. - // We make requests to APIs which don't impact the component under test, e.g. UI metric telemetry, - // and we can mock them all with a 200 instead of mocking each one individually. - server.respondWith([200, {}, 'DefaultSinonMockServerResponse']); - - const httpRequestsMockHelpers = registerHttpRequestMockHelpers(server); + const httpSetup = httpServiceMock.createSetupContract(); + const httpRequestsMockHelpers = registerHttpRequestMockHelpers(httpSetup); return { - server, + httpSetup, httpRequestsMockHelpers, }; }; diff --git a/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx b/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx index 722b1b1ec2cea..02d4f713871a3 100644 --- a/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx +++ b/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx @@ -10,8 +10,6 @@ import './jest.mocks'; import React, { FunctionComponent } from 'react'; -import axios from 'axios'; -import axiosXhrAdapter from 'axios/lib/adapters/xhr'; import { merge } from 'lodash'; import { notificationServiceMock, uiSettingsServiceMock } from '../../../../../core/public/mocks'; @@ -23,7 +21,6 @@ import { init as initHttpRequests } from './http_requests'; import { fieldFormatsMock as fieldFormats } from '../../../../field_formats/common/mocks'; import { FieldFormat } from '../../../../field_formats/common'; -const mockHttpClient = axios.create({ adapter: axiosXhrAdapter }); const dataStart = dataPluginMock.createStartContract(); const { search } = dataStart; @@ -61,12 +58,11 @@ search.search = spySearchQuery; let apiService: ApiService; export const setupEnvironment = () => { - // @ts-expect-error Axios does not fullfill HttpSetupn from core but enough for our tests - apiService = initApi(mockHttpClient); - const { server, httpRequestsMockHelpers } = initHttpRequests(); + const { httpSetup, httpRequestsMockHelpers } = initHttpRequests(); + apiService = initApi(httpSetup); return { - server, + server: httpSetup, httpRequestsMockHelpers, }; }; From 64910b312265561f3422a033987f6d91bc40cb77 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 11 Apr 2022 15:22:52 +0300 Subject: [PATCH 34/42] [Cases] Fix flaky test on e2e view case (#129558) --- .../test/functional/services/cases/create.ts | 5 +++- x-pack/test/functional/services/cases/list.ts | 27 ++++++++++++++++--- .../apps/cases/list_view.ts | 13 ++++----- .../apps/cases/view_case.ts | 3 +-- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/x-pack/test/functional/services/cases/create.ts b/x-pack/test/functional/services/cases/create.ts index f1a54aec75438..5ed22ad51ad9f 100644 --- a/x-pack/test/functional/services/cases/create.ts +++ b/x-pack/test/functional/services/cases/create.ts @@ -12,6 +12,7 @@ export function CasesCreateViewServiceProvider({ getService, getPageObject }: Ft const testSubjects = getService('testSubjects'); const find = getService('find'); const comboBox = getService('comboBox'); + const config = getService('config'); return { /** @@ -57,7 +58,9 @@ export function CasesCreateViewServiceProvider({ getService, getPageObject }: Ft // save await testSubjects.click('create-case-submit'); - await testSubjects.existOrFail('case-view-title'); + await testSubjects.existOrFail('case-view-title', { + timeout: config.get('timeouts.waitFor'), + }); }, }; } diff --git a/x-pack/test/functional/services/cases/list.ts b/x-pack/test/functional/services/cases/list.ts index 87e2fdcb91edc..29fc34d3f0dd5 100644 --- a/x-pack/test/functional/services/cases/list.ts +++ b/x-pack/test/functional/services/cases/list.ts @@ -16,6 +16,7 @@ export function CasesTableServiceProvider({ getService, getPageObject }: FtrProv const find = getService('find'); const header = getPageObject('header'); const retry = getService('retry'); + const config = getService('config'); return { /** @@ -25,8 +26,27 @@ export function CasesTableServiceProvider({ getService, getPageObject }: FtrProv */ async goToFirstListedCase() { await testSubjects.existOrFail('cases-table'); + await testSubjects.existOrFail('case-details-link', { + timeout: config.get('timeouts.waitFor'), + }); await testSubjects.click('case-details-link'); - await testSubjects.existOrFail('case-view-title'); + await testSubjects.existOrFail('case-view-title', { + timeout: config.get('timeouts.waitFor'), + }); + }, + + async deleteFirstListedCase() { + await testSubjects.existOrFail('action-delete', { + timeout: config.get('timeouts.waitFor'), + }); + await testSubjects.click('action-delete'); + await testSubjects.existOrFail('confirmModalConfirmButton', { + timeout: config.get('timeouts.waitFor'), + }); + await testSubjects.click('confirmModalConfirmButton'); + await testSubjects.existOrFail('euiToastHeader', { + timeout: config.get('timeouts.waitFor'), + }); }, async bulkDeleteAllCases() { @@ -55,8 +75,8 @@ export function CasesTableServiceProvider({ getService, getPageObject }: FtrProv }, async validateCasesTableHasNthRows(nrRows: number) { - await retry.tryForTime(2000, async () => { - const rows = await find.allByCssSelector('[data-test-subj*="cases-table-row-"', 100); + await retry.tryForTime(3000, async () => { + const rows = await find.allByCssSelector('[data-test-subj*="cases-table-row-"'); expect(rows.length).equal(nrRows); }); }, @@ -66,6 +86,7 @@ export function CasesTableServiceProvider({ getService, getPageObject }: FtrProv this.refreshTable(); return await testSubjects.exists('case-details-link'); }); + await header.waitUntilLoadingHasFinished(); }, async waitForCasesToBeDeleted() { diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/list_view.ts b/x-pack/test/functional_with_es_ssl/apps/cases/list_view.ts index c1ec21b694b49..e1f06f8a33e9f 100644 --- a/x-pack/test/functional_with_es_ssl/apps/cases/list_view.ts +++ b/x-pack/test/functional_with_es_ssl/apps/cases/list_view.ts @@ -6,7 +6,6 @@ */ import expect from '@kbn/expect'; -import uuid from 'uuid'; import { FtrProviderContext } from '../../ftr_provider_context'; import { CaseStatuses } from '../../../../plugins/cases/common'; @@ -64,9 +63,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('deletes a case correctly from the list', async () => { - await testSubjects.click('action-delete'); - await testSubjects.click('confirmModalConfirmButton'); - await testSubjects.existOrFail('euiToastHeader'); + await cases.casesTable.deleteFirstListedCase(); await cases.casesTable.waitForTableToFinishLoading(); await retry.tryForTime(2000, async () => { @@ -83,13 +80,13 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); describe('filtering', () => { - const id = uuid.v4(); - const caseTitle = 'matchme-' + id; + const caseTitle = 'matchme'; before(async () => { - await cases.api.createNthRandomCases(2); await cases.api.createCase({ title: caseTitle, tags: ['one'] }); - await cases.api.createCase({ tags: ['two'] }); + await cases.api.createCase({ title: 'test2', tags: ['two'] }); + await cases.api.createCase({ title: 'test3' }); + await cases.api.createCase({ title: 'test4' }); await header.waitUntilLoadingHasFinished(); await cases.casesTable.waitForCasesToBeListed(); }); diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/view_case.ts b/x-pack/test/functional_with_es_ssl/apps/cases/view_case.ts index 8fd5821ffdef1..8c14e7e1263ee 100644 --- a/x-pack/test/functional_with_es_ssl/apps/cases/view_case.ts +++ b/x-pack/test/functional_with_es_ssl/apps/cases/view_case.ts @@ -19,8 +19,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { const retry = getService('retry'); const comboBox = getService('comboBox'); - // Failing: See https://github.com/elastic/kibana/issues/129248 - describe.skip('View case', () => { + describe('View case', () => { describe('properties', () => { // create the case to test on before(async () => { From 1bdae99180f37d805170f72867639b57db8b732d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Mon, 11 Apr 2022 11:19:43 -0400 Subject: [PATCH 35/42] [APM] fix error count e2e flaky test (#129755) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../power_user/rules/error_count.spec.ts | 6 ++-- x-pack/plugins/apm/scripts/test/e2e.js | 33 +++++++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/rules/error_count.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/rules/error_count.spec.ts index e53b84ca76496..27830a4f74892 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/rules/error_count.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/rules/error_count.spec.ts @@ -51,12 +51,14 @@ describe('Rules', () => { cy.contains('Alerts and rules').click(); cy.contains('Create error count rule').click(); - // Check for the existence of this element to make sure the form + // Check for the existence of these elements to make sure the form // has loaded. cy.contains('for the last'); + cy.contains('Actions'); + cy.contains('Save').should('not.be.disabled'); // Save, with no actions - cy.contains('button:not(:disabled)', 'Save').click(); + cy.contains('Save').click(); cy.get(confirmModalButtonSelector).click(); cy.contains(`Created rule "${ruleName}`); diff --git a/x-pack/plugins/apm/scripts/test/e2e.js b/x-pack/plugins/apm/scripts/test/e2e.js index 148d5011b1ecb..a4a0cc79cac2d 100644 --- a/x-pack/plugins/apm/scripts/test/e2e.js +++ b/x-pack/plugins/apm/scripts/test/e2e.js @@ -6,7 +6,7 @@ */ /* eslint-disable no-console */ - +const { times } = require('lodash'); const path = require('path'); const yargs = require('yargs'); const childProcess = require('child_process'); @@ -45,6 +45,10 @@ const { argv } = yargs(process.argv.slice(2)) type: 'boolean', description: 'stop tests after the first failure', }) + .option('times', { + type: 'number', + description: 'Repeat the test n number of times', + }) .help(); const { server, runner, open, grep, bail, kibanaInstallDir } = argv; @@ -63,5 +67,28 @@ const grepArg = grep ? `--grep "${grep}"` : ''; const bailArg = bail ? `--bail` : ''; const cmd = `node ../../../../scripts/${ftrScript} --config ${config} ${grepArg} ${bailArg} --kibana-install-dir '${kibanaInstallDir}'`; -console.log(`Running "${cmd}"`); -childProcess.execSync(cmd, { cwd: e2eDir, stdio: 'inherit' }); +function runTests() { + console.log(`Running "${cmd}"`); + childProcess.execSync(cmd, { cwd: e2eDir, stdio: 'inherit' }); +} + +if (argv.times) { + const runCounter = { succeeded: 0, failed: 0, remaining: argv.times }; + let exitStatus = 0; + times(argv.times, () => { + try { + runTests(); + runCounter.succeeded++; + } catch (e) { + exitStatus = 1; + runCounter.failed++; + } + runCounter.remaining--; + if (argv.times > 1) { + console.log(runCounter); + } + }); + process.exit(exitStatus); +} else { + runTests(); +} From d7114900f522cba9580b5385a7f2f32a844f0758 Mon Sep 17 00:00:00 2001 From: Tre Date: Mon, 11 Apr 2022 16:31:46 +0100 Subject: [PATCH 36/42] [QA][Code Coverage] Ingest the correct id for the ci job (#129622) Resolves: https://github.com/elastic/kibana/issues/129617 Use the build number instead of the uuid. --- .buildkite/scripts/steps/code_coverage/ingest.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildkite/scripts/steps/code_coverage/ingest.sh b/.buildkite/scripts/steps/code_coverage/ingest.sh index a9b95cccb533c..f806b6fa149c2 100755 --- a/.buildkite/scripts/steps/code_coverage/ingest.sh +++ b/.buildkite/scripts/steps/code_coverage/ingest.sh @@ -52,4 +52,4 @@ echo "--- Upload coverage static site" .buildkite/scripts/steps/code_coverage/reporting/uploadStaticSite.sh echo "--- Ingest results to Kibana stats cluster" -.buildkite/scripts/steps/code_coverage/reporting/ingestData.sh 'elastic+kibana+code-coverage' ${BUILDKITE_BUILD_ID} ${BUILDKITE_BUILD_URL} ${previousSha} 'src/dev/code_coverage/ingest_coverage/team_assignment/team_assignments.txt' +.buildkite/scripts/steps/code_coverage/reporting/ingestData.sh 'elastic+kibana+code-coverage' ${BUILDKITE_BUILD_NUMBER} ${BUILDKITE_BUILD_URL} ${previousSha} 'src/dev/code_coverage/ingest_coverage/team_assignment/team_assignments.txt' From 980a97f145668676e3937f75c02ba8226817ee6b Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Mon, 11 Apr 2022 17:43:04 +0200 Subject: [PATCH 37/42] Add the authentication tab to the user details page (#129456) * Add the authentication tab to the user details page * Update i18n * Please code review * Add MatrixHistogram to authentications tab Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../integration/users/users_tabs.spec.ts | 65 +++-- .../security_solution/cypress/tasks/login.ts | 2 +- .../authentications_host_table.tsx | 2 +- .../authentications_user_table.test.tsx | 25 ++ .../authentications_user_table.tsx | 24 +- .../components/authentication/helpers.tsx | 78 +++++- .../components/authentication/translations.ts | 28 ++- .../common/components/authentication/types.ts | 26 +- .../{hosts => common}/authentication.ts | 0 .../authentications_query_tab_body.tsx | 49 +--- .../public/users/pages/constants.ts | 2 +- .../users/pages/details/details_tabs.tsx | 4 + .../public/users/pages/details/nav_tabs.tsx | 6 + .../public/users/pages/index.tsx | 9 +- .../authentications_query_tab_body.tsx | 45 ++-- .../translations/translations/fr-FR.json | 233 +++++++++--------- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../es_archives/users/data.json | 6 + 19 files changed, 354 insertions(+), 252 deletions(-) rename x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/{hosts => common}/authentication.ts (100%) diff --git a/x-pack/plugins/security_solution/cypress/integration/users/users_tabs.spec.ts b/x-pack/plugins/security_solution/cypress/integration/users/users_tabs.spec.ts index 8981e2be121b9..6ae79f41ffc60 100644 --- a/x-pack/plugins/security_solution/cypress/integration/users/users_tabs.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/users/users_tabs.spec.ts @@ -20,7 +20,7 @@ import { RISK_SCORE_TAB, RISK_SCORE_TAB_CONTENT } from '../../screens/users/user import { cleanKibana } from '../../tasks/common'; import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; -import { login, visit } from '../../tasks/login'; +import { login, visit, visitUserDetailsPage } from '../../tasks/login'; import { USERS_URL } from '../../urls/navigation'; @@ -35,43 +35,56 @@ describe('Users stats and tables', () => { esArchiverUnload('users'); }); - it(`renders all users`, () => { - const totalUsers = 1; + describe('Users page tabs', () => { + it(`renders all users`, () => { + const totalUsers = 1; - cy.get(HEADER_SUBTITLE).should('have.text', `Showing: ${totalUsers} user`); - }); + cy.get(HEADER_SUBTITLE).should('have.text', `Showing: ${totalUsers} user`); + }); - it(`renders all authentications`, () => { - const totalUsers = 1; + it(`renders all authentications`, () => { + const totalUsers = 1; - cy.get(AUTHENTICATIONS_TAB).click(); + cy.get(AUTHENTICATIONS_TAB).click(); - cy.get(AUTHENTICATIONS_TABLE) - .find(HEADER_SUBTITLE) - .should('have.text', `Showing: ${totalUsers} user`); - }); + cy.get(AUTHENTICATIONS_TABLE) + .find(HEADER_SUBTITLE) + .should('have.text', `Showing: ${totalUsers} user`); + }); - it(`renders anomalies tab`, () => { - cy.get(ANOMALIES_TAB).click({ force: true }); + it(`renders anomalies tab`, () => { + cy.get(ANOMALIES_TAB).click({ force: true }); - cy.get(ANOMALIES_TAB_CONTENT).should('exist'); - }); + cy.get(ANOMALIES_TAB_CONTENT).should('exist'); + }); - it(`renders events tab`, () => { - cy.get(EVENTS_TAB).click({ force: true }); + it(`renders events tab`, () => { + cy.get(EVENTS_TAB).click({ force: true }); - cy.get(EVENTS_TAB_CONTENT).should('exist'); - }); + cy.get(EVENTS_TAB_CONTENT).should('exist'); + }); + + it(`renders external alerts tab`, () => { + cy.get(EXTERNAL_ALERTS_TAB).click({ force: true }); + + cy.get(EXTERNAL_ALERTS_TAB_CONTENT).should('exist'); + }); - it(`renders external alerts tab`, () => { - cy.get(EXTERNAL_ALERTS_TAB).click({ force: true }); + it(`renders users risk tab`, () => { + cy.get(RISK_SCORE_TAB).click({ force: true }); - cy.get(EXTERNAL_ALERTS_TAB_CONTENT).should('exist'); + cy.get(RISK_SCORE_TAB_CONTENT).should('exist'); + }); }); - it(`renders users risk tab`, () => { - cy.get(RISK_SCORE_TAB).click({ force: true }); + describe('User details tabs', () => { + it(`renders authentications tab`, () => { + visitUserDetailsPage(); + const totalUsers = 1; - cy.get(RISK_SCORE_TAB_CONTENT).should('exist'); + cy.get(AUTHENTICATIONS_TABLE) + .find(HEADER_SUBTITLE) + .should('have.text', `Showing: ${totalUsers} host`); + }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/tasks/login.ts b/x-pack/plugins/security_solution/cypress/tasks/login.ts index 27f60ff16f5da..f2beaa28ab8b6 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/login.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/login.ts @@ -352,7 +352,7 @@ export const visitHostDetailsPage = (hostName = 'suricata-iowa') => { cy.get('[data-test-subj="loading-spinner"]').should('not.exist'); }; -export const visitUserDetailsPage = (userName = 'bob') => { +export const visitUserDetailsPage = (userName = 'test') => { visit(userDetailsUrl(userName)); }; diff --git a/x-pack/plugins/security_solution/public/common/components/authentication/authentications_host_table.tsx b/x-pack/plugins/security_solution/public/common/components/authentication/authentications_host_table.tsx index 710b862570086..a62947e19ba7e 100644 --- a/x-pack/plugins/security_solution/public/common/components/authentication/authentications_host_table.tsx +++ b/x-pack/plugins/security_solution/public/common/components/authentication/authentications_host_table.tsx @@ -115,7 +115,7 @@ const AuthenticationsHostTableComponent: React.FC = ( dataTestSubj="authentications-host-table" headerCount={totalCount} headerTitle={i18n.AUTHENTICATIONS} - headerUnit={i18n.UNIT(totalCount)} + headerUnit={i18n.USERS_UNIT(totalCount)} id={TABLE_QUERY_ID} isInspect={isInspected} itemsPerRow={rowItems} diff --git a/x-pack/plugins/security_solution/public/common/components/authentication/authentications_user_table.test.tsx b/x-pack/plugins/security_solution/public/common/components/authentication/authentications_user_table.test.tsx index 6be9c630a20bf..45aa387ca1bcc 100644 --- a/x-pack/plugins/security_solution/public/common/components/authentication/authentications_user_table.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/authentication/authentications_user_table.test.tsx @@ -14,6 +14,7 @@ import { useAuthentications } from '../../../common/containers/authentications'; import { useQueryToggle } from '../../../common/containers/query_toggle'; import { AuthenticationsUserTable } from './authentications_user_table'; import { usersModel } from '../../../users/store'; +import { AuthStackByField } from '../../../../common/search_strategy'; jest.mock('../../../common/containers/query_toggle', () => ({ useQueryToggle: jest.fn().mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() }), @@ -82,4 +83,28 @@ describe('Authentication User Table Component', () => { ); expect(mockUseAuthentications.mock.calls[0][0].skip).toEqual(true); }); + + describe('useAuthentications', () => { + it('calls useAuthentications stacked by username when username is undefined', () => { + render( + + + + ); + expect(mockUseAuthentications.mock.calls[0][0].stackByField).toEqual( + AuthStackByField.userName + ); + }); + + it('calls useAuthentications stacked by hostname when there username is defined', () => { + render( + + + + ); + expect(mockUseAuthentications.mock.calls[0][0].stackByField).toEqual( + AuthStackByField.hostName + ); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/authentication/authentications_user_table.tsx b/x-pack/plugins/security_solution/public/common/components/authentication/authentications_user_table.tsx index 572c06ef4da90..312670038e96e 100644 --- a/x-pack/plugins/security_solution/public/common/components/authentication/authentications_user_table.tsx +++ b/x-pack/plugins/security_solution/public/common/components/authentication/authentications_user_table.tsx @@ -12,20 +12,22 @@ import { useDispatch } from 'react-redux'; import { AuthStackByField } from '../../../../common/search_strategy/security_solution/users/authentications'; import { PaginatedTable } from '../paginated_table'; -import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features'; - import * as i18n from './translations'; -import { getHostsPageAuthenticationColumns, rowItems } from './helpers'; +import { + getUserDetailsAuthenticationColumns, + getUsersPageAuthenticationColumns, + rowItems, +} from './helpers'; import { useAuthentications } from '../../containers/authentications'; import { useQueryInspector } from '../page/manage_query'; import { useQueryToggle } from '../../containers/query_toggle'; import { useDeepEqualSelector } from '../../hooks/use_selector'; import { usersActions, usersModel, usersSelectors } from '../../../users/store'; -import { UsersComponentsQueryProps } from '../../../users/pages/navigation/types'; +import { AuthenticationsUserTableProps } from './types'; const TABLE_QUERY_ID = 'authenticationsUsersTableQuery'; -const AuthenticationsUserTableComponent: React.FC = ({ +const AuthenticationsUserTableComponent: React.FC = ({ docValueFields, endDate, filterQuery, @@ -35,9 +37,8 @@ const AuthenticationsUserTableComponent: React.FC = ( type, setQuery, deleteQuery, + userName, }) => { - const usersEnabled = useIsExperimentalFeatureEnabled('usersEnabled'); - const dispatch = useDispatch(); const { toggleStatus } = useQueryToggle(TABLE_QUERY_ID); const [querySkip, setQuerySkip] = useState(skip || !toggleStatus); @@ -60,10 +61,13 @@ const AuthenticationsUserTableComponent: React.FC = ( startDate, activePage, limit, - stackByField: AuthStackByField.userName, + stackByField: userName ? AuthStackByField.hostName : AuthStackByField.userName, }); - const columns = getHostsPageAuthenticationColumns(usersEnabled); + const columns = useMemo( + () => (userName ? getUserDetailsAuthenticationColumns() : getUsersPageAuthenticationColumns()), + [userName] + ); const updateLimitPagination = useCallback( (newLimit) => @@ -105,7 +109,7 @@ const AuthenticationsUserTableComponent: React.FC = ( dataTestSubj="table-users-authentications" headerCount={totalCount} headerTitle={i18n.AUTHENTICATIONS} - headerUnit={i18n.UNIT(totalCount)} + headerUnit={userName ? i18n.HOSTS_UNIT(totalCount) : i18n.USERS_UNIT(totalCount)} id={TABLE_QUERY_ID} isInspect={isInspected} itemsPerRow={rowItems} diff --git a/x-pack/plugins/security_solution/public/common/components/authentication/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/authentication/helpers.tsx index d541e0e452943..bff5072043527 100644 --- a/x-pack/plugins/security_solution/public/common/components/authentication/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/authentication/helpers.tsx @@ -19,8 +19,15 @@ import { getRowItemDraggables } from '../tables/helpers'; import * as i18n from './translations'; import { HostDetailsLink, NetworkDetailsLink, UserDetailsLink } from '../links'; -import { AuthenticationsEdges } from '../../../../common/search_strategy'; +import { AuthenticationsEdges, MatrixHistogramType } from '../../../../common/search_strategy'; import { AuthTableColumns } from './types'; +import { + MatrixHistogramConfigs, + MatrixHistogramMappingTypes, + MatrixHistogramOption, +} from '../matrix_histogram/types'; +import { LensAttributes } from '../visualization_actions/types'; +import { authenticationLensAttributes } from '../visualization_actions/lens_attributes/common/authentication'; export const getHostDetailsAuthenticationColumns = (usersEnabled: boolean): AuthTableColumns => [ getUserColumn(usersEnabled), @@ -44,6 +51,19 @@ export const getHostsPageAuthenticationColumns = (usersEnabled: boolean): AuthTa LAST_FAILED_DESTINATION_COLUMN, ]; +export const getUsersPageAuthenticationColumns = (): AuthTableColumns => + getHostsPageAuthenticationColumns(true); + +export const getUserDetailsAuthenticationColumns = (): AuthTableColumns => [ + HOST_COLUMN, + SUCCESS_COLUMN, + FAILURES_COLUMN, + LAST_SUCCESSFUL_TIME_COLUMN, + LAST_SUCCESSFUL_SOURCE_COLUMN, + LAST_FAILED_TIME_COLUMN, + LAST_FAILED_SOURCE_COLUMN, +]; + export const rowItems: ItemsPerRow[] = [ { text: i18n.ROWS_5, @@ -177,6 +197,19 @@ const getUserColumn = ( }), }); +const HOST_COLUMN: Columns = { + name: i18n.HOST, + truncateText: false, + mobileOptions: { show: true }, + render: ({ node }) => + getRowItemDraggables({ + rowItems: node.stackedValue, + attrName: 'host.name', + idPrefix: `authentications-table-${node._id}-hostName`, + render: (item) => , + }), +}; + const SUCCESS_COLUMN: Columns = { name: i18n.SUCCESSES, truncateText: false, @@ -215,3 +248,46 @@ const SUCCESS_COLUMN: Columns = { }, width: '8%', }; + +export const authenticationsStackByOptions: MatrixHistogramOption[] = [ + { + text: 'event.outcome', + value: 'event.outcome', + }, +]; +const DEFAULT_STACK_BY = 'event.outcome'; + +enum AuthenticationsMatrixDataGroup { + authenticationsSuccess = 'success', + authenticationsFailure = 'failure', +} + +export enum ChartColors { + authenticationsSuccess = '#54B399', + authenticationsFailure = '#E7664C', +} + +export const authenticationsMatrixDataMappingFields: MatrixHistogramMappingTypes = { + [AuthenticationsMatrixDataGroup.authenticationsSuccess]: { + key: AuthenticationsMatrixDataGroup.authenticationsSuccess, + value: null, + color: ChartColors.authenticationsSuccess, + }, + [AuthenticationsMatrixDataGroup.authenticationsFailure]: { + key: AuthenticationsMatrixDataGroup.authenticationsFailure, + value: null, + color: ChartColors.authenticationsFailure, + }, +}; + +export const histogramConfigs: MatrixHistogramConfigs = { + defaultStackByOption: + authenticationsStackByOptions.find((o) => o.text === DEFAULT_STACK_BY) ?? + authenticationsStackByOptions[0], + errorMessage: i18n.ERROR_FETCHING_AUTHENTICATIONS_DATA, + histogramType: MatrixHistogramType.authentications, + mapping: authenticationsMatrixDataMappingFields, + stackByOptions: authenticationsStackByOptions, + title: i18n.NAVIGATION_AUTHENTICATIONS_TITLE, + lensAttributes: authenticationLensAttributes as LensAttributes, +}; diff --git a/x-pack/plugins/security_solution/public/common/components/authentication/translations.ts b/x-pack/plugins/security_solution/public/common/components/authentication/translations.ts index 7dba26277eaa6..57566eec40c37 100644 --- a/x-pack/plugins/security_solution/public/common/components/authentication/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/authentication/translations.ts @@ -14,12 +14,18 @@ export const AUTHENTICATIONS = i18n.translate( } ); -export const UNIT = (totalCount: number) => - i18n.translate('xpack.securitySolution.authenticationsTable.unit', { +export const USERS_UNIT = (totalCount: number) => + i18n.translate('xpack.securitySolution.authenticationsTable.usersUnit', { values: { totalCount }, defaultMessage: `{totalCount, plural, =1 {user} other {users}}`, }); +export const HOSTS_UNIT = (totalCount: number) => + i18n.translate('xpack.securitySolution.authenticationsTable.hostsUnit', { + values: { totalCount }, + defaultMessage: `{totalCount, plural, =1 {host} other {hosts}}`, + }); + export const LAST_SUCCESSFUL_SOURCE = i18n.translate( 'xpack.securitySolution.authenticationsTable.lastSuccessfulSource', { @@ -74,6 +80,10 @@ export const USER = i18n.translate('xpack.securitySolution.authenticationsTable. defaultMessage: 'User', }); +export const HOST = i18n.translate('xpack.securitySolution.authenticationsTable.host', { + defaultMessage: 'Host', +}); + export const ROWS_5 = i18n.translate('xpack.securitySolution.authenticationsTable.rows', { values: { numRows: 5 }, defaultMessage: '{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}', @@ -83,3 +93,17 @@ export const ROWS_10 = i18n.translate('xpack.securitySolution.authenticationsTab values: { numRows: 10 }, defaultMessage: '{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}', }); + +export const NAVIGATION_AUTHENTICATIONS_TITLE = i18n.translate( + 'xpack.securitySolution.hosts.navigation.authenticationsTitle', + { + defaultMessage: 'Authentications', + } +); + +export const ERROR_FETCHING_AUTHENTICATIONS_DATA = i18n.translate( + 'xpack.securitySolution.hosts.navigaton.matrixHistogram.errorFetchingAuthenticationsData', + { + defaultMessage: 'Failed to query authentications data', + } +); diff --git a/x-pack/plugins/security_solution/public/common/components/authentication/types.ts b/x-pack/plugins/security_solution/public/common/components/authentication/types.ts index c686263fa8d12..4c1000dc8e1ac 100644 --- a/x-pack/plugins/security_solution/public/common/components/authentication/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/authentication/types.ts @@ -6,26 +6,10 @@ */ import { AuthenticationsEdges } from '../../../../common/search_strategy'; +import { UsersComponentsQueryProps } from '../../../users/pages/navigation/types'; import { Columns } from '../paginated_table'; -export type AuthTableColumns = - | [ - Columns, - Columns, - Columns, - Columns, - Columns, - Columns, - Columns, - Columns, - Columns - ] - | [ - Columns, - Columns, - Columns, - Columns, - Columns, - Columns, - Columns - ]; +export type AuthTableColumns = Array>; +export interface AuthenticationsUserTableProps extends UsersComponentsQueryProps { + userName?: string; +} diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/authentication.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/authentication.ts similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/authentication.ts rename to x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/authentication.ts diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/authentications_query_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/authentications_query_tab_body.tsx index 0f6de80343b11..7f97c196ee172 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/authentications_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/authentications_query_tab_body.tsx @@ -7,59 +7,12 @@ import React from 'react'; import { HostsComponentsQueryProps } from './types'; -import { - MatrixHistogramOption, - MatrixHistogramMappingTypes, - MatrixHistogramConfigs, -} from '../../../common/components/matrix_histogram/types'; import { MatrixHistogram } from '../../../common/components/matrix_histogram'; -import { HostsKpiChartColors } from '../../components/kpi_hosts/types'; -import * as i18n from '../translations'; -import { MatrixHistogramType } from '../../../../common/search_strategy/security_solution'; -import { authenticationLensAttributes } from '../../../common/components/visualization_actions/lens_attributes/hosts/authentication'; -import { LensAttributes } from '../../../common/components/visualization_actions/types'; import { AuthenticationsHostTable } from '../../../common/components/authentication/authentications_host_table'; +import { histogramConfigs } from '../../../common/components/authentication/helpers'; const HISTOGRAM_QUERY_ID = 'authenticationsHistogramQuery'; -const authenticationsStackByOptions: MatrixHistogramOption[] = [ - { - text: 'event.outcome', - value: 'event.outcome', - }, -]; -const DEFAULT_STACK_BY = 'event.outcome'; - -enum AuthenticationsMatrixDataGroup { - authenticationsSuccess = 'success', - authenticationsFailure = 'failure', -} - -export const authenticationsMatrixDataMappingFields: MatrixHistogramMappingTypes = { - [AuthenticationsMatrixDataGroup.authenticationsSuccess]: { - key: AuthenticationsMatrixDataGroup.authenticationsSuccess, - value: null, - color: HostsKpiChartColors.authenticationsSuccess, - }, - [AuthenticationsMatrixDataGroup.authenticationsFailure]: { - key: AuthenticationsMatrixDataGroup.authenticationsFailure, - value: null, - color: HostsKpiChartColors.authenticationsFailure, - }, -}; - -const histogramConfigs: MatrixHistogramConfigs = { - defaultStackByOption: - authenticationsStackByOptions.find((o) => o.text === DEFAULT_STACK_BY) ?? - authenticationsStackByOptions[0], - errorMessage: i18n.ERROR_FETCHING_AUTHENTICATIONS_DATA, - histogramType: MatrixHistogramType.authentications, - mapping: authenticationsMatrixDataMappingFields, - stackByOptions: authenticationsStackByOptions, - title: i18n.NAVIGATION_AUTHENTICATIONS_TITLE, - lensAttributes: authenticationLensAttributes as LensAttributes, -}; - const AuthenticationsQueryTabBodyComponent: React.FC = ({ deleteQuery, docValueFields, diff --git a/x-pack/plugins/security_solution/public/users/pages/constants.ts b/x-pack/plugins/security_solution/public/users/pages/constants.ts index 39d8c1546c863..44d3b0ba83e1f 100644 --- a/x-pack/plugins/security_solution/public/users/pages/constants.ts +++ b/x-pack/plugins/security_solution/public/users/pages/constants.ts @@ -12,4 +12,4 @@ export const usersDetailsPagePath = `${USERS_PATH}/:detailName`; export const usersTabPath = `${USERS_PATH}/:tabName(${UsersTableType.allUsers}|${UsersTableType.authentications}|${UsersTableType.anomalies}|${UsersTableType.risk}|${UsersTableType.events}|${UsersTableType.alerts})`; -export const usersDetailsTabPath = `${usersDetailsPagePath}/:tabName(${UsersTableType.anomalies}|${UsersTableType.events}|${UsersTableType.alerts})`; +export const usersDetailsTabPath = `${usersDetailsPagePath}/:tabName(${UsersTableType.authentications}|${UsersTableType.anomalies}|${UsersTableType.events}|${UsersTableType.alerts})`; diff --git a/x-pack/plugins/security_solution/public/users/pages/details/details_tabs.tsx b/x-pack/plugins/security_solution/public/users/pages/details/details_tabs.tsx index e23cac75b6cdf..d3c3a4607b39c 100644 --- a/x-pack/plugins/security_solution/public/users/pages/details/details_tabs.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/details/details_tabs.tsx @@ -20,6 +20,7 @@ import { TimelineId } from '../../../../common/types'; import { EventsQueryTabBody } from '../../../common/components/events_tab/events_query_tab_body'; import { AlertsView } from '../../../common/components/alerts_viewer'; import { userNameExistsFilter } from './helpers'; +import { AuthenticationsQueryTabBody } from '../navigation'; export const UsersDetailsTabs = React.memo( ({ @@ -84,6 +85,9 @@ export const UsersDetailsTabs = React.memo( return ( + + + diff --git a/x-pack/plugins/security_solution/public/users/pages/details/nav_tabs.tsx b/x-pack/plugins/security_solution/public/users/pages/details/nav_tabs.tsx index 9671bd4ee38d0..ee070f749925e 100644 --- a/x-pack/plugins/security_solution/public/users/pages/details/nav_tabs.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/details/nav_tabs.tsx @@ -19,6 +19,12 @@ export const navTabsUsersDetails = ( hasMlUserPermissions: boolean ): UsersDetailsNavTab => { const userDetailsNavTabs = { + [UsersTableType.authentications]: { + id: UsersTableType.authentications, + name: i18n.NAVIGATION_AUTHENTICATIONS_TITLE, + href: getTabsOnUsersDetailsUrl(userName, UsersTableType.authentications), + disabled: false, + }, [UsersTableType.anomalies]: { id: UsersTableType.anomalies, name: i18n.NAVIGATION_ANOMALIES_TITLE, diff --git a/x-pack/plugins/security_solution/public/users/pages/index.tsx b/x-pack/plugins/security_solution/public/users/pages/index.tsx index 0449ab59688fa..04b1122a39b54 100644 --- a/x-pack/plugins/security_solution/public/users/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/index.tsx @@ -12,13 +12,8 @@ import { UsersTableType } from '../store/model'; import { Users } from './users'; import { UsersDetails } from './details'; import { usersDetailsPagePath, usersDetailsTabPath, usersTabPath } from './constants'; -import { useMlCapabilities } from '../../common/components/ml/hooks/use_ml_capabilities'; -import { hasMlUserPermissions } from '../../../common/machine_learning/has_ml_user_permissions'; export const UsersContainer = React.memo(() => { - const capabilities = useMlCapabilities(); - const hasMlPermissions = hasMlUserPermissions(capabilities); - return ( @@ -48,9 +43,7 @@ export const UsersContainer = React.memo(() => { }) => ( diff --git a/x-pack/plugins/security_solution/public/users/pages/navigation/authentications_query_tab_body.tsx b/x-pack/plugins/security_solution/public/users/pages/navigation/authentications_query_tab_body.tsx index 453ae6e9a8f3f..044a0e4de67ee 100644 --- a/x-pack/plugins/security_solution/public/users/pages/navigation/authentications_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/navigation/authentications_query_tab_body.tsx @@ -6,11 +6,14 @@ */ import React from 'react'; -import { UsersComponentsQueryProps } from './types'; import { AuthenticationsUserTable } from '../../../common/components/authentication/authentications_user_table'; - +import { histogramConfigs } from '../../../common/components/authentication/helpers'; +import { AuthenticationsUserTableProps } from '../../../common/components/authentication/types'; +import { MatrixHistogram } from '../../../common/components/matrix_histogram'; export const ID = 'usersAuthenticationsQuery'; +const HISTOGRAM_QUERY_ID = 'usersAuthenticationsHistogramQuery'; + export const AuthenticationsQueryTabBody = ({ endDate, filterQuery, @@ -21,19 +24,33 @@ export const AuthenticationsQueryTabBody = ({ type, docValueFields, deleteQuery, -}: UsersComponentsQueryProps) => { + userName, +}: AuthenticationsUserTableProps) => { return ( - + <> + + + + ); }; diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 8d800c8ba38aa..b6cc88c5a8c92 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -2108,13 +2108,127 @@ "data.search.timeoutContactAdmin": "Votre requête a expiré. Contactez l'administrateur système pour accroître le temps d'exécution.", "data.search.timeoutIncreaseSetting": "Votre requête a expiré. Augmentez le temps d'exécution en utilisant le paramètre avancé de délai d'expiration de la recherche.", "data.search.timeoutIncreaseSettingActionText": "Modifier le paramètre", - "unifiedSearch.search.unableToGetSavedQueryToastTitle": "Impossible de charger la requête enregistrée {savedQueryId}", "data.searchSession.warning.readDocs": "En savoir plus", "data.searchSessionIndicator.noCapability": "Vous n'êtes pas autorisé à créer des sessions de recherche.", "data.searchSessions.sessionService.sessionEditNameError": "Échec de modification du nom de la session de recherche", "data.searchSessions.sessionService.sessionObjectFetchError": "Échec de récupération des informations de la session de recherche", "data.triggers.applyFilterDescription": "Lorsque le filtre Kibana est appliqué. Peut être un filtre simple ou un filtre de plage.", "data.triggers.applyFilterTitle": "Appliquer le filtre", + "unifiedSearch.search.unableToGetSavedQueryToastTitle": "Impossible de charger la requête enregistrée {savedQueryId}", + "unifiedSearch.noDataPopover.content": "Cette plage temporelle ne contient pas de données. Étendez ou ajustez la plage temporelle pour obtenir plus de champs et pouvoir créer des graphiques.", + "unifiedSearch.noDataPopover.dismissAction": "Ne plus afficher", + "unifiedSearch.noDataPopover.subtitle": "Conseil", + "unifiedSearch.noDataPopover.title": "Ensemble de données vide", + "unifiedSearch.query.queryBar.clearInputLabel": "Effacer l'entrée", + "unifiedSearch.query.queryBar.comboboxAriaLabel": "Rechercher et filtrer la page {pageType}", + "unifiedSearch.query.queryBar.kqlFullLanguageName": "Langage de requête Kibana", + "unifiedSearch.query.queryBar.kqlLanguageName": "KQL", + "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoDocLinkText": "documents", + "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoOptOutText": "Ne plus afficher", + "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoText": "Il semblerait que votre requête porte sur un champ imbriqué. Selon le résultat visé, il existe plusieurs façons de construire une syntaxe KQL pour des requêtes imbriquées. Apprenez-en plus avec notre {link}.", + "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoTitle": "Syntaxe de requête imbriquée KQL", + "unifiedSearch.query.queryBar.kqlOffLabel": "Désactivé", + "unifiedSearch.query.queryBar.kqlOnLabel": "Activé", + "unifiedSearch.query.queryBar.languageSwitcher.toText": "Passer au langage de requête Kibana pour la recherche", + "unifiedSearch.query.queryBar.luceneLanguageName": "Lucene", + "unifiedSearch.query.queryBar.searchInputAriaLabel": "Commencer à taper pour rechercher et filtrer la page {pageType}", + "unifiedSearch.query.queryBar.searchInputPlaceholder": "Recherche", + "unifiedSearch.query.queryBar.syntaxOptionsDescription": "{docsLink} (KQL) offre une syntaxe de requête simplifiée et la prise en charge des champs scriptés. KQL offre également une fonctionnalité de saisie semi-automatique. Si vous désactivez KQL, {nonKqlModeHelpText}.", + "unifiedSearch.query.queryBar.syntaxOptionsDescription.nonKqlModeHelpText": "Kibana utilise Lucene.", + "unifiedSearch.search.searchBar.savedQueryDescriptionLabelText": "Description", + "unifiedSearch.search.searchBar.savedQueryDescriptionText": "Enregistrez le texte et les filtres de la requête que vous souhaitez réutiliser.", + "unifiedSearch.search.searchBar.savedQueryForm.titleConflictText": "Ce nom est en conflit avec une requête enregistrée existante.", + "unifiedSearch.search.searchBar.savedQueryFormCancelButtonText": "Annuler", + "unifiedSearch.search.searchBar.savedQueryFormSaveButtonText": "Enregistrer", + "unifiedSearch.search.searchBar.savedQueryFormTitle": "Enregistrer la requête", + "unifiedSearch.search.searchBar.savedQueryIncludeFiltersLabelText": "Inclure les filtres", + "unifiedSearch.search.searchBar.savedQueryIncludeTimeFilterLabelText": "Inclure le filtre temporel", + "unifiedSearch.search.searchBar.savedQueryNameHelpText": "Un nom est requis. Le nom ne peut pas contenir d'espace vide au début ou à la fin. Le nom doit être unique.", + "unifiedSearch.search.searchBar.savedQueryNameLabelText": "Nom", + "unifiedSearch.search.searchBar.savedQueryNoSavedQueriesText": "Aucune requête enregistrée.", + "unifiedSearch.search.searchBar.savedQueryPopoverButtonText": "Voir les requêtes enregistrées", + "unifiedSearch.search.searchBar.savedQueryPopoverClearButtonAriaLabel": "Effacer la requête enregistrée en cours", + "unifiedSearch.search.searchBar.savedQueryPopoverClearButtonText": "Effacer", + "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionCancelButtonText": "Annuler", + "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionConfirmButtonText": "Supprimer", + "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionTitle": "Supprimer \"{savedQueryName}\" ?", + "unifiedSearch.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel": "Supprimer la requête enregistrée {savedQueryName}", + "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonAriaLabel": "Enregistrer en tant que nouvelle requête enregistrée", + "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonText": "Enregistrer en tant que nouvelle", + "unifiedSearch.search.searchBar.savedQueryPopoverSaveButtonAriaLabel": "Enregistrer une nouvelle requête enregistrée", + "unifiedSearch.search.searchBar.savedQueryPopoverSaveButtonText": "Enregistrer la requête en cours", + "unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonAriaLabel": "Enregistrer les modifications apportées à {title}", + "unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonText": "Enregistrer les modifications", + "unifiedSearch.search.searchBar.savedQueryPopoverSavedQueryListItemButtonAriaLabel": "Bouton de requête enregistrée {savedQueryName}", + "unifiedSearch.search.searchBar.savedQueryPopoverSavedQueryListItemDescriptionAriaLabel": "Description de {savedQueryName}", + "unifiedSearch.search.searchBar.savedQueryPopoverSavedQueryListItemSelectedButtonAriaLabel": "Bouton de requête enregistrée {savedQueryName} sélectionné. Appuyez pour effacer les modifications.", + "unifiedSearch.search.searchBar.savedQueryPopoverTitleText": "Requêtes enregistrées", + "unifiedSearch.query.queryBar.syntaxOptionsTitle": "Options de syntaxe", + "unifiedSearch.filter.applyFilterActionTitle": "Appliquer le filtre à la vue en cours", + "unifiedSearch.filter.applyFilters.popupHeader": "Sélectionner les filtres à appliquer", + "unifiedSearch.filter.applyFiltersPopup.cancelButtonLabel": "Annuler", + "unifiedSearch.filter.applyFiltersPopup.saveButtonLabel": "Appliquer", + "unifiedSearch.filter.filterBar.addFilterButtonLabel": "Ajouter un filtre", + "unifiedSearch.filter.filterBar.deleteFilterButtonLabel": "Supprimer", + "unifiedSearch.filter.filterBar.disabledFilterPrefix": "Désactivé", + "unifiedSearch.filter.filterBar.disableFilterButtonLabel": "Désactiver temporairement", + "unifiedSearch.filter.filterBar.editFilterButtonLabel": "Modifier le filtre", + "unifiedSearch.filter.filterBar.enableFilterButtonLabel": "Réactiver", + "unifiedSearch.filter.filterBar.excludeFilterButtonLabel": "Exclure les résultats", + "unifiedSearch.filter.filterBar.filterItemBadgeAriaLabel": "Actions de filtrage", + "unifiedSearch.filter.filterBar.filterItemBadgeIconAriaLabel": "Supprimer {filter}", + "unifiedSearch.filter.filterBar.includeFilterButtonLabel": "Inclure les résultats", + "unifiedSearch.filter.filterBar.indexPatternSelectPlaceholder": "Sélectionner un modèle d'indexation", + "unifiedSearch.filter.filterBar.labelErrorInfo": "Modèle d'indexation {indexPattern} introuvable", + "unifiedSearch.filter.filterBar.labelErrorText": "Erreur", + "unifiedSearch.filter.filterBar.labelWarningInfo": "Le champ {fieldName} n'existe pas dans la vue en cours.", + "unifiedSearch.filter.filterBar.labelWarningText": "Avertissement", + "unifiedSearch.filter.filterBar.moreFilterActionsMessage": "Filtre : {innerText}. Sélectionner pour plus d’actions de filtrage.", + "unifiedSearch.filter.filterBar.negatedFilterPrefix": "NON ", + "unifiedSearch.filter.filterBar.pinFilterButtonLabel": "Épingler dans toutes les applications", + "unifiedSearch.filter.filterBar.pinnedFilterPrefix": "Épinglé", + "unifiedSearch.filter.filterBar.unpinFilterButtonLabel": "Désépingler", + "unifiedSearch.filter.filterEditor.cancelButtonLabel": "Annuler", + "unifiedSearch.filter.filterEditor.createCustomLabelInputLabel": "Étiquette personnalisée", + "unifiedSearch.filter.filterEditor.createCustomLabelSwitchLabel": "Créer une étiquette personnalisée ?", + "unifiedSearch.filter.filterEditor.doesNotExistOperatorOptionLabel": "n'existe pas", + "unifiedSearch.filter.filterEditor.editFilterPopupTitle": "Modifier le filtre", + "unifiedSearch.filter.filterEditor.editFilterValuesButtonLabel": "Modifier les valeurs du filtre", + "unifiedSearch.filter.filterEditor.editQueryDslButtonLabel": "Modifier en tant que Query DSL", + "unifiedSearch.filter.filterEditor.existsOperatorOptionLabel": "existe", + "unifiedSearch.filter.filterEditor.falseOptionLabel": "false", + "unifiedSearch.filter.filterEditor.fieldSelectLabel": "Champ", + "unifiedSearch.filter.filterEditor.fieldSelectPlaceholder": "Sélectionner d'abord un champ", + "unifiedSearch.filter.filterEditor.indexPatternSelectLabel": "Modèle d'indexation", + "unifiedSearch.filter.filterEditor.isBetweenOperatorOptionLabel": "est entre", + "unifiedSearch.filter.filterEditor.isNotBetweenOperatorOptionLabel": "n'est pas entre", + "unifiedSearch.filter.filterEditor.isNotOneOfOperatorOptionLabel": "n'est pas l'une des options suivantes", + "unifiedSearch.filter.filterEditor.isNotOperatorOptionLabel": "n'est pas", + "unifiedSearch.filter.filterEditor.isOneOfOperatorOptionLabel": "est l'une des options suivantes", + "unifiedSearch.filter.filterEditor.isOperatorOptionLabel": "est", + "unifiedSearch.filter.filterEditor.operatorSelectLabel": "Opérateur", + "unifiedSearch.filter.filterEditor.operatorSelectPlaceholderSelect": "Sélectionner", + "unifiedSearch.filter.filterEditor.operatorSelectPlaceholderWaiting": "En attente", + "unifiedSearch.filter.filterEditor.queryDslLabel": "Query DSL d'Elasticsearch", + "unifiedSearch.filter.filterEditor.rangeEndInputPlaceholder": "Fin de la plage", + "unifiedSearch.filter.filterEditor.rangeInputLabel": "Plage", + "unifiedSearch.filter.filterEditor.rangeStartInputPlaceholder": "Début de la plage", + "unifiedSearch.filter.filterEditor.saveButtonLabel": "Enregistrer", + "unifiedSearch.filter.filterEditor.trueOptionLabel": "vrai", + "unifiedSearch.filter.filterEditor.valueInputLabel": "Valeur", + "unifiedSearch.filter.filterEditor.valueInputPlaceholder": "Saisir une valeur", + "unifiedSearch.filter.filterEditor.valueSelectPlaceholder": "Sélectionner une valeur", + "unifiedSearch.filter.filterEditor.valuesSelectLabel": "Valeurs", + "unifiedSearch.filter.filterEditor.valuesSelectPlaceholder": "Sélectionner des valeurs", + "unifiedSearch.filter.options.changeAllFiltersButtonLabel": "Changer tous les filtres", + "unifiedSearch.filter.options.deleteAllFiltersButtonLabel": "Tout supprimer", + "unifiedSearch.filter.options.disableAllFiltersButtonLabel": "Tout désactiver", + "unifiedSearch.filter.options.enableAllFiltersButtonLabel": "Tout activer", + "unifiedSearch.filter.options.invertDisabledFiltersButtonLabel": "Inverser l’activation/désactivation", + "unifiedSearch.filter.options.invertNegatedFiltersButtonLabel": "Inverser l'inclusion", + "unifiedSearch.filter.options.pinAllFiltersButtonLabel": "Tout épingler", + "unifiedSearch.filter.options.unpinAllFiltersButtonLabel": "Tout désépingler", + "unifiedSearch.filter.searchBar.changeAllFiltersTitle": "Changer tous les filtres", "devTools.badge.readOnly.text": "Lecture seule", "devTools.badge.readOnly.tooltip": "Enregistrement impossible", "devTools.devToolsTitle": "Outils de développement", @@ -19659,7 +19773,6 @@ "xpack.securitySolution.authenticationsTable.rows": "{numRows} {numRows, plural, =0 {ligne} =1 {ligne} other {lignes}}", "xpack.securitySolution.authenticationsTable.successes": "Réussites", "xpack.securitySolution.authenticationsTable.uncommonProcessTable": "Processus inhabituels", - "xpack.securitySolution.authenticationsTable.unit": "{totalCount, plural, =1 {utilisateur} other {utilisateurs}}", "xpack.securitySolution.authenticationsTable.user": "Utilisateur", "xpack.securitySolution.authz.mlUnavailable": "Le plug-in de Machine Learning n'est pas disponible. Essayez d'activer le plug-in.", "xpack.securitySolution.authz.userIsNotMlAdminMessage": "L'utilisateur actuel n'est pas un administrateur de Machine Learning.", @@ -25413,120 +25526,6 @@ "xpack.watcher.watchActions.webhook.portIsRequiredValidationMessage": "Le port webhook est requis.", "xpack.watcher.watchActions.webhook.usernameIsRequiredIfPasswordValidationMessage": "Le nom d'utilisateur est requis.", "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "Ce champ est requis.", - "xpack.watcher.watcherDescription": "Détectez les modifications survenant dans vos données en créant, gérant et monitorant des alertes.", - "unifiedSearch.noDataPopover.content": "Cette plage temporelle ne contient pas de données. Étendez ou ajustez la plage temporelle pour obtenir plus de champs et pouvoir créer des graphiques.", - "unifiedSearch.noDataPopover.dismissAction": "Ne plus afficher", - "unifiedSearch.noDataPopover.subtitle": "Conseil", - "unifiedSearch.noDataPopover.title": "Ensemble de données vide", - "unifiedSearch.query.queryBar.clearInputLabel": "Effacer l'entrée", - "unifiedSearch.query.queryBar.comboboxAriaLabel": "Rechercher et filtrer la page {pageType}", - "unifiedSearch.query.queryBar.kqlFullLanguageName": "Langage de requête Kibana", - "unifiedSearch.query.queryBar.kqlLanguageName": "KQL", - "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoDocLinkText": "documents", - "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoOptOutText": "Ne plus afficher", - "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoText": "Il semblerait que votre requête porte sur un champ imbriqué. Selon le résultat visé, il existe plusieurs façons de construire une syntaxe KQL pour des requêtes imbriquées. Apprenez-en plus avec notre {link}.", - "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoTitle": "Syntaxe de requête imbriquée KQL", - "unifiedSearch.query.queryBar.kqlOffLabel": "Désactivé", - "unifiedSearch.query.queryBar.kqlOnLabel": "Activé", - "unifiedSearch.query.queryBar.languageSwitcher.toText": "Passer au langage de requête Kibana pour la recherche", - "unifiedSearch.query.queryBar.luceneLanguageName": "Lucene", - "unifiedSearch.query.queryBar.searchInputAriaLabel": "Commencer à taper pour rechercher et filtrer la page {pageType}", - "unifiedSearch.query.queryBar.searchInputPlaceholder": "Recherche", - "unifiedSearch.query.queryBar.syntaxOptionsDescription": "{docsLink} (KQL) offre une syntaxe de requête simplifiée et la prise en charge des champs scriptés. KQL offre également une fonctionnalité de saisie semi-automatique. Si vous désactivez KQL, {nonKqlModeHelpText}.", - "unifiedSearch.query.queryBar.syntaxOptionsDescription.nonKqlModeHelpText": "Kibana utilise Lucene.", - "unifiedSearch.search.searchBar.savedQueryDescriptionLabelText": "Description", - "unifiedSearch.search.searchBar.savedQueryDescriptionText": "Enregistrez le texte et les filtres de la requête que vous souhaitez réutiliser.", - "unifiedSearch.search.searchBar.savedQueryForm.titleConflictText": "Ce nom est en conflit avec une requête enregistrée existante.", - "unifiedSearch.search.searchBar.savedQueryFormCancelButtonText": "Annuler", - "unifiedSearch.search.searchBar.savedQueryFormSaveButtonText": "Enregistrer", - "unifiedSearch.search.searchBar.savedQueryFormTitle": "Enregistrer la requête", - "unifiedSearch.search.searchBar.savedQueryIncludeFiltersLabelText": "Inclure les filtres", - "unifiedSearch.search.searchBar.savedQueryIncludeTimeFilterLabelText": "Inclure le filtre temporel", - "unifiedSearch.search.searchBar.savedQueryNameHelpText": "Un nom est requis. Le nom ne peut pas contenir d'espace vide au début ou à la fin. Le nom doit être unique.", - "unifiedSearch.search.searchBar.savedQueryNameLabelText": "Nom", - "unifiedSearch.search.searchBar.savedQueryNoSavedQueriesText": "Aucune requête enregistrée.", - "unifiedSearch.search.searchBar.savedQueryPopoverButtonText": "Voir les requêtes enregistrées", - "unifiedSearch.search.searchBar.savedQueryPopoverClearButtonAriaLabel": "Effacer la requête enregistrée en cours", - "unifiedSearch.search.searchBar.savedQueryPopoverClearButtonText": "Effacer", - "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionCancelButtonText": "Annuler", - "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionConfirmButtonText": "Supprimer", - "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionTitle": "Supprimer \"{savedQueryName}\" ?", - "unifiedSearch.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel": "Supprimer la requête enregistrée {savedQueryName}", - "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonAriaLabel": "Enregistrer en tant que nouvelle requête enregistrée", - "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonText": "Enregistrer en tant que nouvelle", - "unifiedSearch.search.searchBar.savedQueryPopoverSaveButtonAriaLabel": "Enregistrer une nouvelle requête enregistrée", - "unifiedSearch.search.searchBar.savedQueryPopoverSaveButtonText": "Enregistrer la requête en cours", - "unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonAriaLabel": "Enregistrer les modifications apportées à {title}", - "unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonText": "Enregistrer les modifications", - "unifiedSearch.search.searchBar.savedQueryPopoverSavedQueryListItemButtonAriaLabel": "Bouton de requête enregistrée {savedQueryName}", - "unifiedSearch.search.searchBar.savedQueryPopoverSavedQueryListItemDescriptionAriaLabel": "Description de {savedQueryName}", - "unifiedSearch.search.searchBar.savedQueryPopoverSavedQueryListItemSelectedButtonAriaLabel": "Bouton de requête enregistrée {savedQueryName} sélectionné. Appuyez pour effacer les modifications.", - "unifiedSearch.search.searchBar.savedQueryPopoverTitleText": "Requêtes enregistrées", - "unifiedSearch.query.queryBar.syntaxOptionsTitle": "Options de syntaxe", - "unifiedSearch.filter.applyFilterActionTitle": "Appliquer le filtre à la vue en cours", - "unifiedSearch.filter.applyFilters.popupHeader": "Sélectionner les filtres à appliquer", - "unifiedSearch.filter.applyFiltersPopup.cancelButtonLabel": "Annuler", - "unifiedSearch.filter.applyFiltersPopup.saveButtonLabel": "Appliquer", - "unifiedSearch.filter.filterBar.addFilterButtonLabel": "Ajouter un filtre", - "unifiedSearch.filter.filterBar.deleteFilterButtonLabel": "Supprimer", - "unifiedSearch.filter.filterBar.disabledFilterPrefix": "Désactivé", - "unifiedSearch.filter.filterBar.disableFilterButtonLabel": "Désactiver temporairement", - "unifiedSearch.filter.filterBar.editFilterButtonLabel": "Modifier le filtre", - "unifiedSearch.filter.filterBar.enableFilterButtonLabel": "Réactiver", - "unifiedSearch.filter.filterBar.excludeFilterButtonLabel": "Exclure les résultats", - "unifiedSearch.filter.filterBar.filterItemBadgeAriaLabel": "Actions de filtrage", - "unifiedSearch.filter.filterBar.filterItemBadgeIconAriaLabel": "Supprimer {filter}", - "unifiedSearch.filter.filterBar.includeFilterButtonLabel": "Inclure les résultats", - "unifiedSearch.filter.filterBar.indexPatternSelectPlaceholder": "Sélectionner un modèle d'indexation", - "unifiedSearch.filter.filterBar.labelErrorInfo": "Modèle d'indexation {indexPattern} introuvable", - "unifiedSearch.filter.filterBar.labelErrorText": "Erreur", - "unifiedSearch.filter.filterBar.labelWarningInfo": "Le champ {fieldName} n'existe pas dans la vue en cours.", - "unifiedSearch.filter.filterBar.labelWarningText": "Avertissement", - "unifiedSearch.filter.filterBar.moreFilterActionsMessage": "Filtre : {innerText}. Sélectionner pour plus d’actions de filtrage.", - "unifiedSearch.filter.filterBar.negatedFilterPrefix": "NON ", - "unifiedSearch.filter.filterBar.pinFilterButtonLabel": "Épingler dans toutes les applications", - "unifiedSearch.filter.filterBar.pinnedFilterPrefix": "Épinglé", - "unifiedSearch.filter.filterBar.unpinFilterButtonLabel": "Désépingler", - "unifiedSearch.filter.filterEditor.cancelButtonLabel": "Annuler", - "unifiedSearch.filter.filterEditor.createCustomLabelInputLabel": "Étiquette personnalisée", - "unifiedSearch.filter.filterEditor.createCustomLabelSwitchLabel": "Créer une étiquette personnalisée ?", - "unifiedSearch.filter.filterEditor.doesNotExistOperatorOptionLabel": "n'existe pas", - "unifiedSearch.filter.filterEditor.editFilterPopupTitle": "Modifier le filtre", - "unifiedSearch.filter.filterEditor.editFilterValuesButtonLabel": "Modifier les valeurs du filtre", - "unifiedSearch.filter.filterEditor.editQueryDslButtonLabel": "Modifier en tant que Query DSL", - "unifiedSearch.filter.filterEditor.existsOperatorOptionLabel": "existe", - "unifiedSearch.filter.filterEditor.falseOptionLabel": "false", - "unifiedSearch.filter.filterEditor.fieldSelectLabel": "Champ", - "unifiedSearch.filter.filterEditor.fieldSelectPlaceholder": "Sélectionner d'abord un champ", - "unifiedSearch.filter.filterEditor.indexPatternSelectLabel": "Modèle d'indexation", - "unifiedSearch.filter.filterEditor.isBetweenOperatorOptionLabel": "est entre", - "unifiedSearch.filter.filterEditor.isNotBetweenOperatorOptionLabel": "n'est pas entre", - "unifiedSearch.filter.filterEditor.isNotOneOfOperatorOptionLabel": "n'est pas l'une des options suivantes", - "unifiedSearch.filter.filterEditor.isNotOperatorOptionLabel": "n'est pas", - "unifiedSearch.filter.filterEditor.isOneOfOperatorOptionLabel": "est l'une des options suivantes", - "unifiedSearch.filter.filterEditor.isOperatorOptionLabel": "est", - "unifiedSearch.filter.filterEditor.operatorSelectLabel": "Opérateur", - "unifiedSearch.filter.filterEditor.operatorSelectPlaceholderSelect": "Sélectionner", - "unifiedSearch.filter.filterEditor.operatorSelectPlaceholderWaiting": "En attente", - "unifiedSearch.filter.filterEditor.queryDslLabel": "Query DSL d'Elasticsearch", - "unifiedSearch.filter.filterEditor.rangeEndInputPlaceholder": "Fin de la plage", - "unifiedSearch.filter.filterEditor.rangeInputLabel": "Plage", - "unifiedSearch.filter.filterEditor.rangeStartInputPlaceholder": "Début de la plage", - "unifiedSearch.filter.filterEditor.saveButtonLabel": "Enregistrer", - "unifiedSearch.filter.filterEditor.trueOptionLabel": "vrai", - "unifiedSearch.filter.filterEditor.valueInputLabel": "Valeur", - "unifiedSearch.filter.filterEditor.valueInputPlaceholder": "Saisir une valeur", - "unifiedSearch.filter.filterEditor.valueSelectPlaceholder": "Sélectionner une valeur", - "unifiedSearch.filter.filterEditor.valuesSelectLabel": "Valeurs", - "unifiedSearch.filter.filterEditor.valuesSelectPlaceholder": "Sélectionner des valeurs", - "unifiedSearch.filter.options.changeAllFiltersButtonLabel": "Changer tous les filtres", - "unifiedSearch.filter.options.deleteAllFiltersButtonLabel": "Tout supprimer", - "unifiedSearch.filter.options.disableAllFiltersButtonLabel": "Tout désactiver", - "unifiedSearch.filter.options.enableAllFiltersButtonLabel": "Tout activer", - "unifiedSearch.filter.options.invertDisabledFiltersButtonLabel": "Inverser l’activation/désactivation", - "unifiedSearch.filter.options.invertNegatedFiltersButtonLabel": "Inverser l'inclusion", - "unifiedSearch.filter.options.pinAllFiltersButtonLabel": "Tout épingler", - "unifiedSearch.filter.options.unpinAllFiltersButtonLabel": "Tout désépingler", - "unifiedSearch.filter.searchBar.changeAllFiltersTitle": "Changer tous les filtres" + "xpack.watcher.watcherDescription": "Détectez les modifications survenant dans vos données en créant, gérant et monitorant des alertes." } } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 641d7debc580e..45eff378b56f9 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -22595,7 +22595,6 @@ "xpack.securitySolution.authenticationsTable.rows": "{numRows} {numRows, plural, other {行}}", "xpack.securitySolution.authenticationsTable.successes": "成功", "xpack.securitySolution.authenticationsTable.uncommonProcessTable": "非共通プロセス", - "xpack.securitySolution.authenticationsTable.unit": "{totalCount, plural, other {ユーザー}}", "xpack.securitySolution.authenticationsTable.user": "ユーザー", "xpack.securitySolution.authz.mlUnavailable": "機械学習プラグインが使用できません。プラグインを有効にしてください。", "xpack.securitySolution.authz.userIsNotMlAdminMessage": "現在のユーザーは機械学習管理者ではありません。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 059e2f68cd5f2..ff9dfba86005e 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -22622,7 +22622,6 @@ "xpack.securitySolution.authenticationsTable.rows": "{numRows} {numRows, plural, other {行}}", "xpack.securitySolution.authenticationsTable.successes": "成功", "xpack.securitySolution.authenticationsTable.uncommonProcessTable": "不常见进程", - "xpack.securitySolution.authenticationsTable.unit": "{totalCount, plural, other {个用户}}", "xpack.securitySolution.authenticationsTable.user": "用户", "xpack.securitySolution.authz.mlUnavailable": "Machine Learning 插件不可用。请尝试启用插件。", "xpack.securitySolution.authz.userIsNotMlAdminMessage": "当前用户不是 Machine Learning 管理员。", diff --git a/x-pack/test/security_solution_cypress/es_archives/users/data.json b/x-pack/test/security_solution_cypress/es_archives/users/data.json index d56e7b0ed8d15..b52ff2780252e 100644 --- a/x-pack/test/security_solution_cypress/es_archives/users/data.json +++ b/x-pack/test/security_solution_cypress/es_archives/users/data.json @@ -29,6 +29,9 @@ }, "name" : "test" }, + "host": { + "name": "test-host" + }, "service" : { "type" : "system" }, @@ -70,6 +73,9 @@ }, "name" : "test" }, + "host": { + "name": "test-host" + }, "service" : { "type" : "system" }, From b239151646c8463ac5d0d0b271f15596406d27e2 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 11 Apr 2022 17:54:57 +0200 Subject: [PATCH 38/42] Handle empty values for range formatters (#129572) --- .../aggs/utils/get_aggs_formats.test.ts | 21 ++++++++ .../search/aggs/utils/get_aggs_formats.ts | 12 +++++ .../public/components/table_vis_columns.tsx | 50 +++++++++---------- 3 files changed, 56 insertions(+), 27 deletions(-) diff --git a/src/plugins/data/common/search/aggs/utils/get_aggs_formats.test.ts b/src/plugins/data/common/search/aggs/utils/get_aggs_formats.test.ts index 963fe024c1f8f..c8d173461cf3b 100644 --- a/src/plugins/data/common/search/aggs/utils/get_aggs_formats.test.ts +++ b/src/plugins/data/common/search/aggs/utils/get_aggs_formats.test.ts @@ -49,6 +49,13 @@ describe('getAggsFormats', () => { expect(getFormat).toHaveBeenCalledTimes(3); }); + test('date_range does not crash on empty value', () => { + const mapping = { id: 'date_range', params: {} }; + const format = getAggFormat(mapping, getFormat); + + expect(format.convert(undefined)).toBe(''); + }); + test('creates custom format for ip_range', () => { const mapping = { id: 'ip_range', params: {} }; const format = getAggFormat(mapping, getFormat); @@ -62,6 +69,13 @@ describe('getAggsFormats', () => { expect(getFormat).toHaveBeenCalledTimes(4); }); + test('ip_range does not crash on empty value', () => { + const mapping = { id: 'ip_range', params: {} }; + const format = getAggFormat(mapping, getFormat); + + expect(format.convert(undefined)).toBe(''); + }); + test('creates custom format for range', () => { const mapping = { id: 'range', params: {} }; const format = getAggFormat(mapping, getFormat); @@ -70,6 +84,13 @@ describe('getAggsFormats', () => { expect(getFormat).toHaveBeenCalledTimes(1); }); + test('range does not crash on empty value', () => { + const mapping = { id: 'range', params: {} }; + const format = getAggFormat(mapping, getFormat); + + expect(format.convert(undefined)).toBe(''); + }); + test('creates alternative format for range using the template parameter', () => { const mapping = { id: 'range', params: { template: 'arrow_right' } }; const format = getAggFormat(mapping, getFormat); diff --git a/src/plugins/data/common/search/aggs/utils/get_aggs_formats.ts b/src/plugins/data/common/search/aggs/utils/get_aggs_formats.ts index 876d7673b87a6..2233c9338cd58 100644 --- a/src/plugins/data/common/search/aggs/utils/get_aggs_formats.ts +++ b/src/plugins/data/common/search/aggs/utils/get_aggs_formats.ts @@ -44,6 +44,10 @@ export function getAggsFormats(getFieldFormat: GetFieldFormat): FieldFormatInsta textConvert = (range: any) => { const params = this._params; + if (range == null) { + return ''; + } + if (range.label) { return range.label; } @@ -90,6 +94,10 @@ export function getAggsFormats(getFieldFormat: GetFieldFormat): FieldFormatInsta static hidden = true; textConvert = (range: DateRange) => { + if (range == null) { + return ''; + } + const nestedFormatter = this._params as SerializedFieldFormat; const format = getFieldFormat({ id: nestedFormatter.id, @@ -103,6 +111,10 @@ export function getAggsFormats(getFieldFormat: GetFieldFormat): FieldFormatInsta static hidden = true; textConvert = (range: IpRangeKey) => { + if (range == null) { + return ''; + } + const nestedFormatter = this._params as SerializedFieldFormat; const format = getFieldFormat({ id: nestedFormatter.id, diff --git a/src/plugins/vis_types/table/public/components/table_vis_columns.tsx b/src/plugins/vis_types/table/public/components/table_vis_columns.tsx index 2abeae31fbea9..17d32264998ea 100644 --- a/src/plugins/vis_types/table/public/components/table_vis_columns.tsx +++ b/src/plugins/vis_types/table/public/components/table_vis_columns.tsx @@ -57,7 +57,7 @@ export const createGridColumns = ( ? [ ({ rowIndex, columnId, Component, closePopover }: EuiDataGridColumnCellActionProps) => { const rowValue = rows[rowIndex][columnId]; - const contentsIsDefined = rowValue !== null && rowValue !== undefined; + if (rowValue == null) return null; const cellContent = formattedColumn.formatter.convert(rowValue); const filterForText = i18n.translate( @@ -77,24 +77,22 @@ export const createGridColumns = ( ); return ( - contentsIsDefined && ( - { - onFilterClick({ row: rowIndex, column: colIndex, value: rowValue }, false); - closePopover?.(); - }} - iconType="plusInCircle" - > - {filterForText} - - ) + { + onFilterClick({ row: rowIndex, column: colIndex, value: rowValue }, false); + closePopover?.(); + }} + iconType="plusInCircle" + > + {filterForText} + ); }, ({ rowIndex, columnId, Component, closePopover }: EuiDataGridColumnCellActionProps) => { const rowValue = rows[rowIndex][columnId]; - const contentsIsDefined = rowValue !== null && rowValue !== undefined; + if (rowValue == null) return null; const cellContent = formattedColumn.formatter.convert(rowValue); const filterOutText = i18n.translate( @@ -114,18 +112,16 @@ export const createGridColumns = ( ); return ( - contentsIsDefined && ( - { - onFilterClick({ row: rowIndex, column: colIndex, value: rowValue }, true); - closePopover?.(); - }} - iconType="minusInCircle" - > - {filterOutText} - - ) + { + onFilterClick({ row: rowIndex, column: colIndex, value: rowValue }, true); + closePopover?.(); + }} + iconType="minusInCircle" + > + {filterOutText} + ); }, ] From 6e8d97948a2acf5d6989efe88da2a1f01d39b8a0 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Mon, 11 Apr 2022 18:11:27 +0200 Subject: [PATCH 39/42] [Fleet] Redesign agent flyout (#128381) * [Fleet] Redesign and refactor agent flyout * Further refactoring * More refactoring and some bug fixing * bug fixing * Fix step tests + skip standalone tab tests * Use test-subj for agent policy code block to fix cypress tests * Fixing small issues on rendering * Fix broken import; Fix button in incoming data comp * Fix loading; add button in incoming data step * Fix some failures * Fix tests * Fix checks * Address code review comments; add retry logic to incoming data component * Fix download link * Check enrolled agents in last 10 minutes * Filter out agents with data=false from incoming data Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Kyle Pollich --- .../cypress/integration/fleet_startup.spec.ts | 7 +- x-pack/plugins/fleet/cypress/screens/fleet.ts | 1 + x-pack/plugins/fleet/cypress/tasks/fleet.ts | 1 - .../fleet_server_on_prem_instructions.tsx | 67 ++-- .../fleet/sections/agents/index.tsx | 1 + .../detail/policies/package_policies.tsx | 75 +--- .../agent_enrollment_flyout.test.mocks.ts | 34 +- .../agent_enrollment_flyout.test.tsx | 90 ++--- .../agent_policy_select_create.tsx | 55 ++- .../agent_policy_selection.test.tsx | 11 +- .../agent_policy_selection.tsx | 55 ++- .../confirm_agent_enrollment.tsx | 102 ++++- .../confirm_incoming_data.tsx | 97 ++--- .../default_missing_requirements.tsx | 35 ++ .../agent_enrollment_flyout/hooks.tsx | 7 +- .../agent_enrollment_flyout/index.tsx | 135 ++++--- .../installation_message.tsx | 59 +++ .../agent_enrollment_flyout/instructions.tsx | 126 ++++++ .../managed_instructions.tsx | 206 ---------- .../standalone_instructions.tsx | 281 ------------- .../agent_enrollment_confirmation_step.tsx | 41 ++ .../agent_enrollment_key_selection_step.tsx | 53 +++ .../steps/agent_policy_selection_step.tsx | 54 +++ .../steps/compute_steps.tsx | 369 ++++++++++++++++++ .../steps/configure_standalone_agent_step.tsx | 116 ++++++ .../{steps.tsx => steps/download_step.tsx} | 110 +----- .../steps/incoming_data_confirmation_step.tsx | 47 +++ .../agent_enrollment_flyout/steps/index.tsx | 17 + .../steps/install_managed_agent_step.tsx | 41 ++ .../steps/install_standalone_agent_step.tsx | 47 +++ .../installation_mode_selection_step.tsx | 88 +++++ .../agent_enrollment_flyout/types.ts | 39 +- .../use_get_agent_incoming_data.tsx | 121 ++++++ .../enrollment_instructions/manual/index.tsx | 27 +- .../manual/platform_selector.tsx | 28 +- .../standalone/index.tsx | 51 +++ .../package_policy_actions_menu.tsx | 10 +- x-pack/plugins/fleet/public/hooks/index.ts | 1 - .../hooks/use_get_agent_incoming_data.tsx | 78 ---- .../fleet/public/hooks/use_platform.tsx | 2 +- .../fleet/server/services/agents/status.ts | 2 +- .../translations/translations/fr-FR.json | 10 - .../translations/translations/ja-JP.json | 10 - .../translations/translations/zh-CN.json | 10 - 44 files changed, 1704 insertions(+), 1113 deletions(-) create mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/default_missing_requirements.tsx create mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/installation_message.tsx create mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx delete mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx delete mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/standalone_instructions.tsx create mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_enrollment_confirmation_step.tsx create mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_enrollment_key_selection_step.tsx create mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_policy_selection_step.tsx create mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx create mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/configure_standalone_agent_step.tsx rename x-pack/plugins/fleet/public/components/agent_enrollment_flyout/{steps.tsx => steps/download_step.tsx} (67%) create mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/incoming_data_confirmation_step.tsx create mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/index.tsx create mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_managed_agent_step.tsx create mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_standalone_agent_step.tsx create mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/installation_mode_selection_step.tsx create mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/use_get_agent_incoming_data.tsx create mode 100644 x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx delete mode 100644 x-pack/plugins/fleet/public/hooks/use_get_agent_incoming_data.tsx diff --git a/x-pack/plugins/fleet/cypress/integration/fleet_startup.spec.ts b/x-pack/plugins/fleet/cypress/integration/fleet_startup.spec.ts index 1505754635bcb..4f7f4378c0fc2 100644 --- a/x-pack/plugins/fleet/cypress/integration/fleet_startup.spec.ts +++ b/x-pack/plugins/fleet/cypress/integration/fleet_startup.spec.ts @@ -10,6 +10,7 @@ import { ADD_AGENT_BUTTON_TOP, AGENT_FLYOUT_CLOSE_BUTTON, STANDALONE_TAB, + AGENT_POLICY_CODE_BLOCK, } from '../screens/fleet'; import { cleanupAgentPolicies, unenrollAgent } from '../tasks/cleanup'; import { verifyPolicy, verifyAgentPackage, navigateToTab } from '../tasks/fleet'; @@ -60,12 +61,11 @@ describe('Fleet startup', () => { cy.log('Create agent policy took: ' + (Date.now() - startTime) / 1000 + ' s'); agentPolicyId = xhr.response.body.item.id; - cy.getBySel('agentPolicyCreateStatusCallOut').contains('Agent policy created'); - // verify create button changed to dropdown cy.getBySel('agentPolicyDropdown'); + // verify agent.yml code block has new policy id - cy.get('.euiCodeBlock__code').contains(`id: ${agentPolicyId}`); + cy.getBySel(AGENT_POLICY_CODE_BLOCK).contains(`id: ${agentPolicyId}`); cy.getBySel(AGENT_FLYOUT_CLOSE_BUTTON).click(); @@ -78,7 +78,6 @@ describe('Fleet startup', () => { it('should create Fleet Server policy', () => { cy.getBySel('createFleetServerPolicyBtn').click(); - cy.getBySel('agentPolicyCreateStatusCallOut').contains('Agent policy created'); // verify policy is created and has fleet server and system package verifyPolicy('Fleet Server policy 1', ['Fleet Server', 'System']); diff --git a/x-pack/plugins/fleet/cypress/screens/fleet.ts b/x-pack/plugins/fleet/cypress/screens/fleet.ts index 32ecdc4f5da71..ef3389b90bd5d 100644 --- a/x-pack/plugins/fleet/cypress/screens/fleet.ts +++ b/x-pack/plugins/fleet/cypress/screens/fleet.ts @@ -9,6 +9,7 @@ export const ADD_AGENT_BUTTON = 'addAgentButton'; export const ADD_AGENT_BUTTON_TOP = 'addAgentBtnTop'; export const CREATE_POLICY_BUTTON = 'createPolicyBtn'; export const AGENT_FLYOUT_CLOSE_BUTTON = 'euiFlyoutCloseButton'; +export const AGENT_POLICY_CODE_BLOCK = 'agentPolicyCodeBlock'; export const AGENTS_TAB = 'fleet-agents-tab'; export const AGENT_POLICIES_TAB = 'fleet-agent-policies-tab'; diff --git a/x-pack/plugins/fleet/cypress/tasks/fleet.ts b/x-pack/plugins/fleet/cypress/tasks/fleet.ts index 304ab7445d4e4..926734503ae0c 100644 --- a/x-pack/plugins/fleet/cypress/tasks/fleet.ts +++ b/x-pack/plugins/fleet/cypress/tasks/fleet.ts @@ -18,7 +18,6 @@ export function createAgentPolicy() { cy.getBySel(ADD_AGENT_BUTTON_TOP).click(); cy.getBySel(STANDALONE_TAB).click(); cy.getBySel(CREATE_POLICY_BUTTON).click(); - cy.getBySel('agentPolicyCreateStatusCallOut').contains('Agent policy created'); cy.getBySel(AGENT_FLYOUT_CLOSE_BUTTON).click(); } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx index 1eef74180fdd3..93547bba36de8 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx @@ -203,7 +203,6 @@ export const FleetServerCommandStep = ({ windowsCommand={installCommand.windows} linuxDebCommand={installCommand.deb} linuxRpmCommand={installCommand.rpm} - troubleshootLink={docLinks.links.fleet.troubleshooting} isK8s={false} /> @@ -290,36 +289,16 @@ export const useFleetServerInstructions = (policyId?: string) => { }; const AgentPolicySelectionStep = ({ - policyId, + selectedPolicy, setPolicyId, + agentPolicies, + refreshAgentPolicies, }: { - policyId?: string; - setPolicyId: (v: string) => void; + selectedPolicy?: AgentPolicy; + setPolicyId: (v?: string) => void; + agentPolicies: AgentPolicy[]; + refreshAgentPolicies: () => void; }): EuiStepProps => { - const { data, resendRequest: refreshAgentPolicies } = useGetAgentPolicies({ full: true }); - - const agentPolicies = useMemo( - () => (data ? data.items.filter((item) => policyHasFleetServer(item)) : []), - [data] - ); - - useEffect(() => { - // Select default value - if (agentPolicies.length && !policyId) { - setPolicyId(agentPolicies[0].id); - } - }, [agentPolicies, policyId, setPolicyId]); - - const onChangeCallback = useCallback( - (key: string | undefined, policy?: AgentPolicy) => { - if (policy) { - refreshAgentPolicies(); - } - setPolicyId(key!); - }, - [setPolicyId, refreshAgentPolicies] - ); - return { title: agentPolicies.length === 0 @@ -335,9 +314,11 @@ const AgentPolicySelectionStep = ({ ), @@ -642,9 +623,28 @@ const CompleteStep = (): EuiStepProps => { }; }; +const findPolicyById = (policies: AgentPolicy[], id: string | undefined) => { + if (!id) return undefined; + return policies.find((p) => p.id === id); +}; + export const OnPremInstructions: React.FC = () => { const { notifications } = useStartServices(); - const [policyId, setPolicyId] = useState(); + + const { data, resendRequest: refreshAgentPolicies } = useGetAgentPolicies({ full: true }); + + const agentPolicies = useMemo( + () => (data ? data.items.filter((item) => policyHasFleetServer(item)) : []), + [data] + ); + + // Select default value + let defaultValue = ''; + if (agentPolicies.length) { + defaultValue = agentPolicies[0].id; + } + const [policyId, setPolicyId] = useState(defaultValue); + const selectedPolicy = findPolicyById(agentPolicies, policyId); const { serviceToken, @@ -720,7 +720,12 @@ export const OnPremInstructions: React.FC = () => { { setIsEnrollmentFlyoutOpen(false)} /> diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx index 7ca852fa9b6e7..3edb60ff10a48 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx @@ -7,11 +7,7 @@ import { stringify, parse } from 'query-string'; import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'; import { Redirect, useLocation, useHistory } from 'react-router-dom'; -import type { - CriteriaWithPagination, - EuiStepProps, - EuiTableFieldDataColumnType, -} from '@elastic/eui'; +import type { CriteriaWithPagination, EuiTableFieldDataColumnType } from '@elastic/eui'; import { EuiBasicTable, EuiLink, @@ -19,7 +15,6 @@ import { EuiFlexItem, EuiText, EuiButton, - EuiSpacer, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedRelative, FormattedMessage } from '@kbn/i18n-react'; @@ -31,7 +26,6 @@ import { useUrlPagination, useGetPackageInstallStatus, AgentPolicyRefreshContext, - useUIExtension, usePackageInstallations, useAuthz, } from '../../../../../hooks'; @@ -103,7 +97,6 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: ${name}`, }); const { updatableIntegrations } = usePackageInstallations(); - const agentEnrollmentFlyoutExtension = useUIExtension(name, 'agent-enrollment-flyout'); const canWriteIntegrationPolicies = useAuthz().integrations.writeIntegrationPolicies; @@ -165,50 +158,6 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps [setPagination] ); - const viewDataStep = useMemo(() => { - if (agentEnrollmentFlyoutExtension) { - return { - title: agentEnrollmentFlyoutExtension.title, - children: , - }; - } - - return { - title: i18n.translate('xpack.fleet.agentEnrollment.stepViewDataTitle', { - defaultMessage: 'View your data', - }), - children: ( - <> - - - {i18n.translate( - 'xpack.fleet.epm.agentEnrollment.viewDataDescription.pleaseNoteLabel', - { defaultMessage: 'Please note' } - )} - - ), - }} - /> - - - - {i18n.translate('xpack.fleet.epm.agentEnrollment.viewDataAssetsLabel', { - defaultMessage: 'View assets', - })} - - - ), - }; - }, [name, version, getHref, agentEnrollmentFlyoutExtension]); - const columns: Array> = useMemo( () => [ { @@ -323,7 +272,6 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps { @@ -362,6 +310,11 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps ); } + const selectedPolicies = packageAndAgentPolicies.find( + ({ agentPolicy: policy }) => policy.id === flyoutOpenForPolicyId + ); + const agentPolicy = selectedPolicies?.agentPolicy; + const packagePolicy = selectedPolicies?.packagePolicy; return ( @@ -379,19 +332,19 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps /> - {flyoutOpenForPolicyId && !isLoading && ( + {flyoutOpenForPolicyId && agentPolicy && !isLoading && ( { setFlyoutOpenForPolicyId(null); const { addAgentToPolicyId, ...rest } = parse(search); history.replace({ search: stringify(rest) }); }} - agentPolicy={ - packageAndAgentPolicies.find( - ({ agentPolicy }) => agentPolicy.id === flyoutOpenForPolicyId - )?.agentPolicy - } - viewDataStep={viewDataStep} + agentPolicy={agentPolicy} + isIntegrationFlow={true} + installedPackagePolicy={{ + name: packagePolicy?.package?.name || '', + version: packagePolicy?.package?.version || '', + }} /> )} diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts index 15f6437485925..408f884116e26 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts @@ -8,6 +8,7 @@ jest.mock('../../hooks', () => { return { ...jest.requireActual('../../hooks'), + useFleetStatus: jest.fn(), useAgentEnrollmentFlyoutData: jest.fn(), }; }); @@ -24,6 +25,16 @@ jest.mock('../../hooks/use_request', () => { }; }); +jest.mock('../../applications/fleet/sections/agents/hooks/use_fleet_server_unhealthy', () => { + const module = jest.requireActual( + '../../applications/fleet/sections/agents/hooks/use_fleet_server_unhealthy' + ); + return { + ...module, + useFleetServerUnhealthy: jest.fn(), + }; +}); + jest.mock( '../../applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions', () => { @@ -57,18 +68,17 @@ jest.mock('./steps', () => { const module = jest.requireActual('./steps'); return { ...module, - AgentPolicySelectionStep: jest.fn(), - AgentEnrollmentKeySelectionStep: jest.fn(), - ViewDataStep: jest.fn(), - DownloadStep: jest.fn(), - }; -}); - -jest.mock('@elastic/eui', () => { - const module = jest.requireActual('@elastic/eui'); - return { - ...module, - EuiSteps: 'eui-steps', + AgentPolicySelectionStep: jest.fn().mockReturnValue({ + 'data-test-subj': 'agent-policy-selection-step', + title: 'agent-policy-selection-step', + }), + AgentEnrollmentKeySelectionStep: jest.fn().mockReturnValue({ + 'data-test-subj': 'agent-enrollment-key-selection-step', + title: 'agent-enrollment-key-selection-step', + }), + DownloadStep: jest + .fn() + .mockReturnValue({ 'data-test-subj': 'download-step', title: 'download-step' }), }; }); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx index 3629fe43969e4..7a4d1ed44c406 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx @@ -16,27 +16,23 @@ import { coreMock } from 'src/core/public/mocks'; import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; import type { AgentPolicy } from '../../../common'; -import { - useGetSettings, - sendGetFleetStatus, - sendGetOneAgentPolicy, - useGetAgents, -} from '../../hooks/use_request'; +import { useGetSettings, sendGetOneAgentPolicy, useGetAgents } from '../../hooks/use_request'; import { FleetStatusProvider, ConfigContext, useAgentEnrollmentFlyoutData, KibanaVersionContext, + useFleetStatus, } from '../../hooks'; import { useFleetServerInstructions } from '../../applications/fleet/sections/agents/agent_requirements_page/components'; -import { AgentEnrollmentKeySelectionStep, AgentPolicySelectionStep } from './steps'; +import { useFleetServerUnhealthy } from '../../applications/fleet/sections/agents/hooks/use_fleet_server_unhealthy'; -import type { Props } from '.'; +import type { FlyOutProps } from './types'; import { AgentEnrollmentFlyout } from '.'; -const TestComponent = (props: Props) => ( +const TestComponent = (props: FlyOutProps) => ( @@ -48,7 +44,7 @@ const TestComponent = (props: Props) => ( ); -const setup = async (props?: Props) => { +const setup = async (props?: FlyOutProps) => { const testBed = await registerTestBed(TestComponent)(props); const { find, component } = testBed; @@ -87,8 +83,10 @@ describe('', () => { data: { item: { fleet_server_hosts: ['test'] } }, }); - (sendGetFleetStatus as jest.Mock).mockResolvedValue({ - data: { isReady: true }, + (useFleetStatus as jest.Mock).mockReturnValue({ isReady: true }); + (useFleetServerUnhealthy as jest.Mock).mockReturnValue({ + isLoading: false, + isUnhealthy: false, }); (sendGetOneAgentPolicy as jest.Mock).mockResolvedValue({ @@ -147,9 +145,10 @@ describe('', () => { describe('managed instructions', () => { it('uses the agent policy selection step', async () => { const { exists } = testBed; + expect(exists('agentEnrollmentFlyout')).toBe(true); - expect(AgentPolicySelectionStep).toHaveBeenCalled(); - expect(AgentEnrollmentKeySelectionStep).not.toHaveBeenCalled(); + expect(exists('agent-policy-selection-step')).toBe(true); + expect(exists('agent-enrollment-key-selection-step')).toBe(false); }); describe('with a specific policy', () => { @@ -167,8 +166,8 @@ describe('', () => { it('uses the configure enrollment step, not the agent policy selection step', () => { const { exists } = testBed; expect(exists('agentEnrollmentFlyout')).toBe(true); - expect(AgentPolicySelectionStep).not.toHaveBeenCalled(); - expect(AgentEnrollmentKeySelectionStep).toHaveBeenCalled(); + expect(exists('agent-policy-selection-step')).toBe(false); + expect(exists('agent-enrollment-key-selection-step')).toBe(true); }); }); @@ -187,58 +186,21 @@ describe('', () => { it('should not show fleet server instructions', () => { const { exists } = testBed; expect(exists('agentEnrollmentFlyout')).toBe(true); - expect(AgentEnrollmentKeySelectionStep).toHaveBeenCalled(); - }); - }); - - // Skipped due to implementation details in the step components. See https://github.com/elastic/kibana/issues/103894 - describe.skip('"View data" extension point', () => { - it('shows the "View data" step when UI extension is provided', async () => { - jest.clearAllMocks(); - await act(async () => { - testBed = await setup({ - onClose: jest.fn(), - viewDataStep: { title: 'View Data', children:
}, - }); - testBed.component.update(); - }); - const { exists, actions } = testBed; - expect(exists('agentEnrollmentFlyout')).toBe(true); - expect(exists('view-data-step')).toBe(true); - - jest.clearAllMocks(); - actions.goToStandaloneTab(); - expect(exists('agentEnrollmentFlyout')).toBe(true); - expect(exists('view-data-step')).toBe(false); - }); - - it('does not call the "View data" step when UI extension is not provided', async () => { - jest.clearAllMocks(); - await act(async () => { - testBed = await setup({ - onClose: jest.fn(), - viewDataStep: undefined, - }); - testBed.component.update(); - }); - const { exists, actions } = testBed; - expect(exists('agentEnrollmentFlyout')).toBe(true); - expect(exists('view-data-step')).toBe(false); - - jest.clearAllMocks(); - actions.goToStandaloneTab(); - expect(exists('view-data-step')).toBe(false); + expect(exists('agent-enrollment-key-selection-step')).toBe(true); }); }); }); - describe('standalone instructions', () => { + // Skipped due to UI changing in https://github.com/elastic/kibana/issues/125534. These tests should be rethought overall + // to provide value around the new flyout structure + describe.skip('standalone instructions', () => { it('uses the agent policy selection step', async () => { const { exists, actions } = testBed; actions.goToStandaloneTab(); + expect(exists('agentEnrollmentFlyout')).toBe(true); - expect(AgentPolicySelectionStep).toHaveBeenCalled(); - expect(AgentEnrollmentKeySelectionStep).not.toHaveBeenCalled(); + expect(exists('agent-policy-selection-step')).toBe(true); + expect(exists('agent-enrollment-key-selection-step')).toBe(false); }); describe('with a specific policy', () => { @@ -256,10 +218,12 @@ describe('', () => { it('does not use either of the agent policy selection or enrollment key steps', () => { const { exists, actions } = testBed; jest.clearAllMocks(); - expect(exists('agentEnrollmentFlyout')).toBe(true); + actions.goToStandaloneTab(); - expect(AgentPolicySelectionStep).not.toHaveBeenCalled(); - expect(AgentEnrollmentKeySelectionStep).not.toHaveBeenCalled(); + + expect(exists('agentEnrollmentFlyout')).toBe(true); + expect(exists('agent-policy-selection-step')).toBe(false); + expect(exists('agent-enrollment-key-selection-step')).toBe(false); }); }); }); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx index 53e8140086c2c..81ec1236920b8 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState, useCallback, useEffect } from 'react'; +import React, { useState, useCallback, useEffect, useMemo } from 'react'; import type { AgentPolicyCreateState } from '../../applications/fleet/sections/agents/components'; import { @@ -16,43 +16,60 @@ import { AgentPolicyCreateInlineForm } from '../../applications/fleet/sections/a import type { AgentPolicy } from '../../types'; import { incrementPolicyName } from '../../services'; -import { EnrollmentStepAgentPolicy } from '.'; +import { AgentPolicySelection } from '.'; interface Props { agentPolicies: AgentPolicy[]; + selectedPolicy?: AgentPolicy; + setSelectedPolicyId: (agentPolicyId?: string) => void; excludeFleetServer?: boolean; - onAgentPolicyChange: (key?: string, policy?: AgentPolicy) => void; withKeySelection: boolean; selectedApiKeyId?: string; onKeyChange?: (key?: string) => void; isFleetServerPolicy?: boolean; - policyId?: string; + refreshAgentPolicies: () => void; } export const SelectCreateAgentPolicy: React.FC = ({ agentPolicies, excludeFleetServer, - onAgentPolicyChange, + setSelectedPolicyId, + selectedPolicy, withKeySelection, selectedApiKeyId, onKeyChange, isFleetServerPolicy, - policyId, + refreshAgentPolicies, }) => { - const [showCreatePolicy, setShowCreatePolicy] = useState(agentPolicies.length === 0); + const regularAgentPolicies = useMemo(() => { + return agentPolicies.filter( + (policy) => + policy && !policy.is_managed && (!excludeFleetServer || !policy.is_default_fleet_server) + ); + }, [agentPolicies, excludeFleetServer]); + + const onAgentPolicyChange = useCallback( + async (key?: string, policy?: AgentPolicy) => { + if (policy) { + refreshAgentPolicies(); + } + }, + [refreshAgentPolicies] + ); + const [showCreatePolicy, setShowCreatePolicy] = useState(regularAgentPolicies.length === 0); const [createState, setCreateState] = useState({ status: CREATE_STATUS.INITIAL, }); - const [newName, setNewName] = useState(incrementPolicyName(agentPolicies, isFleetServerPolicy)); - - const [selectedAgentPolicy, setSelectedAgentPolicy] = useState(policyId); + const [newName, setNewName] = useState( + incrementPolicyName(regularAgentPolicies, isFleetServerPolicy) + ); useEffect(() => { - setShowCreatePolicy(agentPolicies.length === 0); - setNewName(incrementPolicyName(agentPolicies, isFleetServerPolicy)); - }, [agentPolicies, isFleetServerPolicy]); + setShowCreatePolicy(regularAgentPolicies.length === 0); + setNewName(incrementPolicyName(regularAgentPolicies, isFleetServerPolicy)); + }, [regularAgentPolicies, isFleetServerPolicy]); const onAgentPolicyCreated = useCallback( async (policy: AgentPolicy | null, errorMessage?: JSX.Element) => { @@ -65,9 +82,9 @@ export const SelectCreateAgentPolicy: React.FC = ({ if (onAgentPolicyChange) { onAgentPolicyChange(policy.id, policy!); } - setSelectedAgentPolicy(policy.id); + setSelectedPolicyId(policy.id); }, - [onAgentPolicyChange] + [setSelectedPolicyId, onAgentPolicyChange] ); const onClickCreatePolicy = () => { @@ -87,15 +104,15 @@ export const SelectCreateAgentPolicy: React.FC = ({ agentPolicyName={newName} /> ) : ( - )} diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.test.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.test.tsx index b84d101fdef5f..c5a6076308525 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.test.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.test.tsx @@ -13,7 +13,7 @@ import { createFleetTestRendererMock } from '../../mock'; import type { AgentPolicy } from '../../types'; -import { EnrollmentStepAgentPolicy } from '.'; +import { AgentPolicySelection } from '.'; describe('step select agent policy', () => { let testRenderer: TestRenderer; @@ -21,10 +21,11 @@ describe('step select agent policy', () => { let agentPolicies: AgentPolicy[] = []; const render = () => (renderResult = testRenderer.render( - { testRenderer = createFleetTestRendererMock(); }); - test('should not select agent policy by default if multiple exists', async () => { + test('should select first agent policy by default if multiple exists', async () => { agentPolicies = [ { id: 'policy-1', name: 'Policy 1' } as AgentPolicy, { id: 'policy-2', name: 'Policy 2' } as AgentPolicy, @@ -45,7 +46,7 @@ describe('step select agent policy', () => { await act(async () => { const select = renderResult.container.querySelector('[data-test-subj="agentPolicyDropdown"]'); - expect((select as any)?.value).toEqual(''); + expect((select as any)?.value).toEqual('policy-1'); expect(renderResult.getAllByRole('option').length).toBe(2); }); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.tsx index 3227308d93b9c..63737adb5caea 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState, useEffect } from 'react'; +import React, { useEffect } from 'react'; import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -34,10 +34,10 @@ const AgentPolicyFormRow = styled(EuiFormRow)` type Props = { agentPolicies: AgentPolicy[]; - onAgentPolicyChange: (key?: string, policy?: AgentPolicy) => void; + selectedPolicy?: AgentPolicy; + setSelectedPolicyId: (agentPolicyId?: string) => void; excludeFleetServer?: boolean; onClickCreatePolicy: () => void; - selectedAgentPolicy?: string; isFleetServerPolicy?: boolean; } & ( | { @@ -63,44 +63,33 @@ const resolveAgentId = ( return selectedAgentPolicyId; }; -export const EnrollmentStepAgentPolicy: React.FC = (props) => { +export const AgentPolicySelection: React.FC = (props) => { const { agentPolicies, - onAgentPolicyChange, + selectedPolicy, + setSelectedPolicyId, excludeFleetServer, onClickCreatePolicy, - selectedAgentPolicy, isFleetServerPolicy, } = props; - const [selectedAgentPolicyId, setSelectedAgentPolicyId] = useState( - () => resolveAgentId(agentPolicies, undefined) // no agent id selected yet - ); - const hasFleetAllPrivileges = useAuthz().fleet.all; - useEffect( - function triggerOnAgentPolicyChangeEffect() { - if (onAgentPolicyChange) { - onAgentPolicyChange(selectedAgentPolicyId); - } - }, - [selectedAgentPolicyId, onAgentPolicyChange] - ); - useEffect( function useDefaultAgentPolicyEffect() { - const resolvedId = resolveAgentId(agentPolicies, selectedAgentPolicyId); - if (resolvedId !== selectedAgentPolicyId) { - setSelectedAgentPolicyId(resolvedId); + const resolvedId = resolveAgentId(agentPolicies, selectedPolicy?.id); + // find AgentPolicy + if (resolvedId !== selectedPolicy?.id) { + setSelectedPolicyId(resolvedId); } }, - [agentPolicies, selectedAgentPolicyId] + [agentPolicies, setSelectedPolicyId, selectedPolicy] ); - useEffect(() => { - if (selectedAgentPolicy) setSelectedAgentPolicyId(selectedAgentPolicy); - }, [selectedAgentPolicy]); + const onChangeCallback = (event: React.ChangeEvent) => { + const { value } = event.target; + setSelectedPolicyId(value); + }; return ( <> @@ -154,24 +143,24 @@ export const EnrollmentStepAgentPolicy: React.FC = (props) => { value: agentPolicy.id, text: agentPolicy.name, }))} - value={selectedAgentPolicyId} - onChange={(e) => setSelectedAgentPolicyId(e.target.value)} + value={selectedPolicy?.id} + onChange={onChangeCallback} aria-label={i18n.translate( 'xpack.fleet.enrollmentStepAgentPolicy.policySelectAriaLabel', { defaultMessage: 'Agent policy', } )} - hasNoInitialSelection={!selectedAgentPolicyId} + hasNoInitialSelection={!selectedPolicy?.id} data-test-subj="agentPolicyDropdown" - isInvalid={!selectedAgentPolicyId} + isInvalid={!selectedPolicy?.id} /> - {selectedAgentPolicyId && !isFleetServerPolicy && ( + {selectedPolicy?.id && !isFleetServerPolicy && ( <> @@ -183,7 +172,7 @@ export const EnrollmentStepAgentPolicy: React.FC = (props) => { selectedApiKeyId={props.selectedApiKeyId} onKeyChange={props.onKeyChange} initialAuthenticationSettingsOpen={!props.selectedApiKeyId} - agentPolicyId={selectedAgentPolicyId} + agentPolicyId={selectedPolicy?.id} /> )} diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/confirm_agent_enrollment.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/confirm_agent_enrollment.tsx index dd39f8700e4fb..63292713daa93 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/confirm_agent_enrollment.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/confirm_agent_enrollment.tsx @@ -5,29 +5,99 @@ * 2.0. */ -import React from 'react'; -import { EuiCallOut, EuiButton } from '@elastic/eui'; +import React, { useEffect, useRef, useState } from 'react'; +import { EuiCallOut, EuiButton, EuiText, EuiLink } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import { useGetAgentStatus } from '../../hooks'; +import { sendGetAgents, useLink, useStartServices } from '../../hooks'; import { AGENTS_PREFIX } from '../../constants'; interface Props { - policyId: string; - onClickViewAgents: () => void; + policyId?: string; + troubleshootLink: string; + onClickViewAgents?: () => void; + agentCount: number; } +const POLLING_INTERVAL_MS = 5 * 1000; // 5 sec + +/** + * Hook for finding agents enrolled since component was rendered. Should be + * used by parent component to power rendering + * @param policyId + * @returns agentIds + */ +export const usePollingAgentCount = (policyId: string) => { + const [agentIds, setAgentIds] = useState([]); + + const timeout = useRef(undefined); + + useEffect(() => { + let isAborted = false; + + const poll = () => { + timeout.current = window.setTimeout(async () => { + const request = await sendGetAgents({ + kuery: `${AGENTS_PREFIX}.policy_id:"${policyId}" and ${AGENTS_PREFIX}.enrolled_at >= now-10m`, + showInactive: false, + }); + + const newAgentIds = request.data?.items.map((i) => i.id) ?? agentIds; + if (newAgentIds.some((id) => !agentIds.includes(id))) { + setAgentIds(newAgentIds); + } + if (!isAborted) { + poll(); + } + }, POLLING_INTERVAL_MS); + }; + + poll(); + + if (isAborted || agentIds.length > 0) clearTimeout(timeout.current); + + return () => { + isAborted = true; + }; + }, [agentIds, policyId]); + return agentIds; +}; + export const ConfirmAgentEnrollment: React.FunctionComponent = ({ policyId, + troubleshootLink, onClickViewAgents, + agentCount, }) => { - // Check the agents enrolled in the last 10 minutes - const enrolledAt = 'now-10m'; - const kuery = `${AGENTS_PREFIX}.enrolled_at >= "${enrolledAt}"`; - const agentStatusRequest = useGetAgentStatus({ kuery, policyId }); - const agentsCount = agentStatusRequest.data?.results?.total; - - if (!agentsCount) { - return null; + const { getHref } = useLink(); + const { application } = useStartServices(); + + const onButtonClick = () => { + if (onClickViewAgents) onClickViewAgents(); + const href = getHref('agent_list'); + application.navigateToUrl(href); + }; + + if (!policyId || agentCount === 0) { + return ( + + + + + ), + }} + /> + + ); } return ( @@ -35,16 +105,16 @@ export const ConfirmAgentEnrollment: React.FunctionComponent = ({ data-test-subj="ConfirmAgentEnrollmentCallOut" title={i18n.translate('xpack.fleet.agentEnrollment.confirmation.title', { defaultMessage: - '{agentsCount} {agentsCount, plural, one {agent has} other {agents have}} been enrolled.', + '{agentCount} {agentCount, plural, one {agent has} other {agents have}} been enrolled.', values: { - agentsCount, + agentCount, }, })} color="success" iconType="check" > diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/confirm_incoming_data.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/confirm_incoming_data.tsx index 85817fa9850a0..b74129e443e45 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/confirm_incoming_data.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/confirm_incoming_data.tsx @@ -9,67 +9,74 @@ import React from 'react'; import { EuiCallOut, EuiText, EuiSpacer, EuiButton } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import type { InstalledIntegrationPolicy } from '../../hooks'; -import { useGetAgentIncomingData } from '../../hooks'; +import type { InstalledIntegrationPolicy } from './use_get_agent_incoming_data'; +import { useGetAgentIncomingData, usePollingIncomingData } from './use_get_agent_incoming_data'; + interface Props { - agentsIds: string[]; + agentIds: string[]; installedPolicy?: InstalledIntegrationPolicy; + agentDataConfirmed: boolean; + setAgentDataConfirmed: (v: boolean) => void; } export const ConfirmIncomingData: React.FunctionComponent = ({ - agentsIds, + agentIds, installedPolicy, + agentDataConfirmed, + setAgentDataConfirmed, }) => { - const { enrolledAgents, numAgentsWithData, isLoading, linkButton } = useGetAgentIncomingData( - agentsIds, + const { incomingData, isLoading } = usePollingIncomingData(agentIds); + + const { enrolledAgents, numAgentsWithData, linkButton, message } = useGetAgentIncomingData( + incomingData, installedPolicy ); + if (!isLoading && enrolledAgents > 0 && numAgentsWithData > 0) { + setAgentDataConfirmed(true); + } + if (!agentDataConfirmed) { + return ( + + {i18n.translate('xpack.fleet.confirmIncomingData.loading', { + defaultMessage: + 'It may take a few minutes for data to arrive in Elasticsearch. If the system is not generating data, it may help to generate some to ensure data is being collected correctly. If you’re having trouble, see our troubleshooting guide. You may close this dialog and check later by viewing our integration assets.', + })} + + ); + } + return ( <> - {isLoading ? ( - - {i18n.translate('xpack.fleet.confirmIncomingData.loading', { - defaultMessage: - 'It may take a few minutes for data to arrive in Elasticsearch. If the system is not generating data, it may help to generate some to ensure data is being collected correctly. If you’re having trouble, see our troubleshooting guide. You may close this dialog and check later by viewing our integration assets.', - })} - - ) : ( + + {installedPolicy && ( <> - - - {i18n.translate('xpack.fleet.confirmIncomingData.subtitle', { - defaultMessage: 'Your agent is enrolled successfully and your data is received.', - })} - + {message} + + + {linkButton.text} + )} - - - {installedPolicy && ( - - {linkButton.text} - - )} ); }; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/default_missing_requirements.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/default_missing_requirements.tsx new file mode 100644 index 0000000000000..d7ae0a441d66f --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/default_missing_requirements.tsx @@ -0,0 +1,35 @@ +/* + * 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 { EuiLink } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { useLink } from '../../hooks'; + +export const DefaultMissingRequirements = () => { + const { getHref } = useLink(); + + return ( + <> + + + + ), + }} + /> + + ); +}; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/hooks.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/hooks.tsx index d7b48b5a961c2..159de12ff578c 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/hooks.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/hooks.tsx @@ -9,8 +9,11 @@ import { i18n } from '@kbn/i18n'; import type { PackagePolicy, AgentPolicy } from '../../types'; import { sendGetOneAgentPolicy, useStartServices } from '../../hooks'; + import { FLEET_KUBERNETES_PACKAGE } from '../../../common'; +import type { K8sMode } from './types'; + export function useAgentPolicyWithPackagePolicies(policyId?: string) { const [agentPolicyWithPackagePolicies, setAgentPolicy] = useState(null); const core = useStartServices(); @@ -41,9 +44,7 @@ export function useAgentPolicyWithPackagePolicies(policyId?: string) { } export function useIsK8sPolicy(agentPolicy?: AgentPolicy) { - const [isK8s, setIsK8s] = useState<'IS_LOADING' | 'IS_KUBERNETES' | 'IS_NOT_KUBERNETES'>( - 'IS_LOADING' - ); + const [isK8s, setIsK8s] = useState('IS_LOADING'); useEffect(() => { async function checkifK8s() { if (!agentPolicy) { diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx index ed4cc15aec196..0adcfb26aa9c2 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx @@ -24,43 +24,42 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { useGetSettings, useFleetStatus, useAgentEnrollmentFlyoutData } from '../../hooks'; import { FLEET_SERVER_PACKAGE } from '../../constants'; -import type { PackagePolicy } from '../../types'; +import type { PackagePolicy, AgentPolicy } from '../../types'; import { Loading } from '..'; -import { ManagedInstructions } from './managed_instructions'; -import { StandaloneInstructions } from './standalone_instructions'; +import { Instructions } from './instructions'; import { MissingFleetServerHostCallout } from './missing_fleet_server_host_callout'; -import type { BaseProps } from './types'; -import { useIsK8sPolicy, useAgentPolicyWithPackagePolicies } from './hooks'; - -type FlyoutMode = 'managed' | 'standalone'; +import type { FlyOutProps, SelectionType, FlyoutMode } from './types'; -export interface Props extends BaseProps { - onClose: () => void; - defaultMode?: FlyoutMode; -} +import { useIsK8sPolicy, useAgentPolicyWithPackagePolicies } from './hooks'; export * from './agent_policy_selection'; export * from './agent_policy_select_create'; -export * from './managed_instructions'; -export * from './standalone_instructions'; +export * from './instructions'; export * from './steps'; -export const AgentEnrollmentFlyout: React.FunctionComponent = ({ +export const AgentEnrollmentFlyout: React.FunctionComponent = ({ onClose, agentPolicy, - viewDataStep, defaultMode = 'managed', + isIntegrationFlow, + installedPackagePolicy, }) => { - const [mode, setMode] = useState(defaultMode); + const findPolicyById = (policies: AgentPolicy[], id: string | undefined) => { + if (!id) return undefined; + return policies.find((p) => p.id === id); + }; const settings = useGetSettings(); + const fleetStatus = useFleetStatus(); const fleetServerHosts = settings.data?.item?.fleet_server_hosts || []; - const fleetStatus = useFleetStatus(); - const [policyId, setSelectedPolicyId] = useState(agentPolicy?.id); + const [selectedPolicyId, setSelectedPolicyId] = useState(agentPolicy?.id); const [isFleetServerPolicySelected, setIsFleetServerPolicySelected] = useState(false); + const [selectedApiKeyId, setSelectedAPIKeyId] = useState(); + const [mode, setMode] = useState(defaultMode); + const [selectionType, setSelectionType] = useState('tabs'); const { agentPolicies, @@ -68,11 +67,19 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ isLoadingAgentPolicies, refreshAgentPolicies, } = useAgentEnrollmentFlyoutData(); - const { agentPolicyWithPackagePolicies } = useAgentPolicyWithPackagePolicies(policyId); + + const { agentPolicyWithPackagePolicies } = useAgentPolicyWithPackagePolicies(selectedPolicyId); + + const selectedPolicy = agentPolicyWithPackagePolicies + ? agentPolicyWithPackagePolicies + : findPolicyById(agentPolicies, selectedPolicyId); + + const hasNoFleetServerHost = fleetStatus.isReady && fleetServerHosts.length === 0; + useEffect(() => { - if (agentPolicyWithPackagePolicies && setIsFleetServerPolicySelected) { + if (selectedPolicy) { if ( - (agentPolicyWithPackagePolicies.package_policies as PackagePolicy[]).some( + (selectedPolicy.package_policies as PackagePolicy[]).some( (packagePolicy) => packagePolicy.package?.name === FLEET_SERVER_PACKAGE ) ) { @@ -81,11 +88,9 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ setIsFleetServerPolicySelected(false); } } - }, [agentPolicyWithPackagePolicies]); + }, [selectedPolicy, isFleetServerPolicySelected]); - const { isK8s } = useIsK8sPolicy( - agentPolicyWithPackagePolicies ? agentPolicyWithPackagePolicies : undefined - ); + const { isK8s } = useIsK8sPolicy(selectedPolicy ? selectedPolicy : undefined); const isLoadingInitialRequest = settings.isLoading && settings.isInitialRequest; @@ -107,61 +112,63 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ defaultMessage="Add Elastic Agents to your hosts to collect data and send it to the Elastic Stack." /> - - - setMode('managed')} - > - - - setMode('standalone')} - > - - - + {selectionType === 'tabs' ? ( + <> + + + setMode('managed')} + > + + + setMode('standalone')} + > + + + + + ) : null} - ) : undefined } > {isLoadingInitialAgentPolicies ? ( - - ) : mode === 'managed' ? ( - + ) : ( + - ) : ( - )} diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/installation_message.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/installation_message.tsx new file mode 100644 index 0000000000000..6bb178921c2a9 --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/installation_message.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 React, { useMemo } from 'react'; +import { EuiText, EuiLink } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import semverMajor from 'semver/functions/major'; +import semverMinor from 'semver/functions/minor'; +import semverPatch from 'semver/functions/patch'; + +import { useKibanaVersion } from '../../hooks'; + +export const InstallationMessage: React.FunctionComponent = () => { + const kibanaVersion = useKibanaVersion(); + const kibanaVersionURLString = useMemo( + () => + `${semverMajor(kibanaVersion)}-${semverMinor(kibanaVersion)}-${semverPatch(kibanaVersion)}`, + [kibanaVersion] + ); + + return ( + + + + + ), + installationLink: ( + + + + ), + }} + /> + + ); +}; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx new file mode 100644 index 0000000000000..32ac217c353e8 --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx @@ -0,0 +1,126 @@ +/* + * 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, { useMemo } from 'react'; +import { EuiText, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { useFleetStatus, useGetAgents } from '../../hooks'; + +import { FleetServerRequirementPage } from '../../applications/fleet/sections/agents/agent_requirements_page'; + +import { policyHasFleetServer } from '../../applications/fleet/sections/agents/services/has_fleet_server'; + +import { FLEET_SERVER_PACKAGE } from '../../constants'; + +import { useFleetServerUnhealthy } from '../../applications/fleet/sections/agents/hooks/use_fleet_server_unhealthy'; + +import { Loading } from '..'; + +import type { InstructionProps } from './types'; + +import { ManagedSteps, StandaloneSteps, FleetServerSteps } from './steps'; +import { DefaultMissingRequirements } from './default_missing_requirements'; + +export const Instructions = (props: InstructionProps) => { + const { + agentPolicies, + isFleetServerPolicySelected, + settings, + isLoadingAgentPolicies, + setSelectionType, + mode, + isIntegrationFlow, + } = props; + const fleetStatus = useFleetStatus(); + const { isUnhealthy: isFleetServerUnhealthy } = useFleetServerUnhealthy(); + + const { data: agents, isLoading: isLoadingAgents } = useGetAgents({ + page: 1, + perPage: 1000, + showInactive: false, + }); + + const fleetServers = useMemo(() => { + const fleetServerAgentPolicies: string[] = agentPolicies + .filter((pol) => policyHasFleetServer(pol)) + .map((pol) => pol.id); + return (agents?.items ?? []).filter((agent) => + fleetServerAgentPolicies.includes(agent.policy_id ?? '') + ); + }, [agents, agentPolicies]); + + const fleetServerHosts = useMemo(() => { + return settings?.fleet_server_hosts || []; + }, [settings]); + + const hasNoFleetServerHost = fleetStatus.isReady && fleetServerHosts.length === 0; + + const showAgentEnrollment = + fleetStatus.isReady && + !isFleetServerUnhealthy && + fleetServers.length > 0 && + fleetServerHosts.length > 0; + + const showFleetServerEnrollment = + fleetServers.length === 0 || + isFleetServerUnhealthy || + (fleetStatus.missingRequirements ?? []).some((r) => r === FLEET_SERVER_PACKAGE); + + if (!isIntegrationFlow && showAgentEnrollment) { + setSelectionType('radio'); + } else { + setSelectionType('tabs'); + } + + if (isLoadingAgents || isLoadingAgentPolicies) return ; + + if (hasNoFleetServerHost) { + return null; + } + + if (mode === 'managed') { + if (showFleetServerEnrollment) { + return ; + } else if (showAgentEnrollment) { + return ( + <> + + + + + {isFleetServerPolicySelected ? ( + + ) : ( + + )} + + ); + } + return ; + } else { + return ; + } +}; + +const StandaloneInstructions = (props: InstructionProps) => { + return ( + <> + + + + + + + ); +}; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx deleted file mode 100644 index 1e2141ea3827f..0000000000000 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx +++ /dev/null @@ -1,206 +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 React, { useState, useMemo } from 'react'; -import { EuiSteps, EuiLink, EuiText, EuiSpacer } from '@elastic/eui'; -import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; - -import { useGetOneEnrollmentAPIKey, useLink, useFleetStatus, useGetAgents } from '../../hooks'; - -import { ManualInstructions } from '../../components/enrollment_instructions'; -import { - deploymentModeStep, - ServiceTokenStep, - FleetServerCommandStep, - useFleetServerInstructions, - addFleetServerHostStep, -} from '../../applications/fleet/sections/agents/agent_requirements_page/components'; -import { FleetServerRequirementPage } from '../../applications/fleet/sections/agents/agent_requirements_page'; - -import { policyHasFleetServer } from '../../applications/fleet/sections/agents/services/has_fleet_server'; - -import { FLEET_SERVER_PACKAGE } from '../../constants'; - -import { DownloadStep, AgentPolicySelectionStep, AgentEnrollmentKeySelectionStep } from './steps'; -import type { InstructionProps } from './types'; - -const DefaultMissingRequirements = () => { - const { getHref } = useLink(); - - return ( - <> - - - - ), - }} - /> - - ); -}; - -const FleetServerMissingRequirements = () => { - return ; -}; - -export const ManagedInstructions = React.memo( - ({ - agentPolicy, - agentPolicies, - viewDataStep, - setSelectedPolicyId, - policyId, - isFleetServerPolicySelected, - isK8s, - settings, - refreshAgentPolicies, - isLoadingAgentPolicies, - }) => { - const fleetStatus = useFleetStatus(); - const [selectedApiKeyId, setSelectedAPIKeyId] = useState(); - const apiKey = useGetOneEnrollmentAPIKey(selectedApiKeyId); - const fleetServerInstructions = useFleetServerInstructions(apiKey?.data?.item?.policy_id); - - const { data: agents, isLoading: isLoadingAgents } = useGetAgents({ - page: 1, - perPage: 1000, - showInactive: false, - }); - - const fleetServers = useMemo(() => { - const fleetServerAgentPolicies: string[] = agentPolicies - .filter((pol) => policyHasFleetServer(pol)) - .map((pol) => pol.id); - return (agents?.items ?? []).filter((agent) => - fleetServerAgentPolicies.includes(agent.policy_id ?? '') - ); - }, [agents, agentPolicies]); - - const fleetServerSteps = useMemo(() => { - const { - serviceToken, - getServiceToken, - isLoadingServiceToken, - installCommand, - platform, - setPlatform, - deploymentMode, - setDeploymentMode, - addFleetServerHost, - } = fleetServerInstructions; - - return [ - deploymentModeStep({ deploymentMode, setDeploymentMode }), - addFleetServerHostStep({ addFleetServerHost }), - ServiceTokenStep({ serviceToken, getServiceToken, isLoadingServiceToken }), - FleetServerCommandStep({ serviceToken, installCommand, platform, setPlatform }), - ]; - }, [fleetServerInstructions]); - - const enrolToken = apiKey.data ? apiKey.data.item.api_key : ''; - - const steps = useMemo(() => { - const fleetServerHosts = settings?.fleet_server_hosts || []; - const baseSteps: EuiContainedStepProps[] = [ - !agentPolicy - ? AgentPolicySelectionStep({ - agentPolicies, - selectedApiKeyId, - setSelectedAPIKeyId, - setSelectedPolicyId, - refreshAgentPolicies, - }) - : AgentEnrollmentKeySelectionStep({ agentPolicy, selectedApiKeyId, setSelectedAPIKeyId }), - DownloadStep(isFleetServerPolicySelected || false, isK8s || '', enrolToken || ''), - ]; - if (isFleetServerPolicySelected) { - baseSteps.push(...fleetServerSteps); - } else { - baseSteps.push({ - title: i18n.translate('xpack.fleet.agentEnrollment.stepEnrollAndRunAgentTitle', { - defaultMessage: 'Enroll and start the Elastic Agent', - }), - children: selectedApiKeyId && apiKey.data && ( - - ), - }); - } - - if (viewDataStep) { - baseSteps.push({ 'data-test-subj': 'view-data-step', ...viewDataStep }); - } - - return baseSteps; - }, [ - agentPolicy, - selectedApiKeyId, - setSelectedPolicyId, - setSelectedAPIKeyId, - agentPolicies, - refreshAgentPolicies, - apiKey.data, - fleetServerSteps, - isFleetServerPolicySelected, - settings?.fleet_server_hosts, - viewDataStep, - enrolToken, - isK8s, - policyId, - ]); - - if (fleetStatus.isReady && settings?.fleet_server_hosts.length === 0) { - return null; - } - - if ( - fleetStatus.isReady && - (isLoadingAgents || isLoadingAgentPolicies || fleetServers.length > 0) - ) { - return ( - <> - - - - - - - ); - } - - const showFleetMissingRequirements = - fleetServers.length === 0 || - (fleetStatus.missingRequirements ?? []).some((r) => r === FLEET_SERVER_PACKAGE); - - return ( - <> - {showFleetMissingRequirements ? ( - - ) : ( - - )} - - ); - } -); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/standalone_instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/standalone_instructions.tsx deleted file mode 100644 index 4df0431252135..0000000000000 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/standalone_instructions.tsx +++ /dev/null @@ -1,281 +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 React, { useState, useEffect } from 'react'; -import { - EuiSteps, - EuiText, - EuiSpacer, - EuiButton, - EuiCode, - EuiFlexItem, - EuiFlexGroup, - EuiCodeBlock, - EuiCopy, - EuiLink, -} from '@elastic/eui'; -import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { safeDump } from 'js-yaml'; - -import { - useStartServices, - useLink, - sendGetOneAgentPolicyFull, - useKibanaVersion, -} from '../../hooks'; -import { fullAgentPolicyToYaml, agentPolicyRouteService } from '../../services'; - -import { PlatformSelector } from '../enrollment_instructions/manual/platform_selector'; - -import { DownloadStep, AgentPolicySelectionStep } from './steps'; -import type { InstructionProps } from './types'; -import { useIsK8sPolicy, useAgentPolicyWithPackagePolicies } from './hooks'; - -export const StandaloneInstructions = React.memo( - ({ agentPolicy, agentPolicies, refreshAgentPolicies }) => { - const { getHref } = useLink(); - const core = useStartServices(); - const { notifications } = core; - - const [selectedPolicyId, setSelectedPolicyId] = useState(agentPolicy?.id); - const [fullAgentPolicy, setFullAgentPolicy] = useState(); - const [yaml, setYaml] = useState(''); - const kibanaVersion = useKibanaVersion(); - - const { agentPolicyWithPackagePolicies } = useAgentPolicyWithPackagePolicies(selectedPolicyId); - const { isK8s } = useIsK8sPolicy( - agentPolicyWithPackagePolicies ? agentPolicyWithPackagePolicies : undefined - ); - - const KUBERNETES_RUN_INSTRUCTIONS = 'kubectl apply -f elastic-agent-standalone-kubernetes.yaml'; - - const STANDALONE_RUN_INSTRUCTIONS_LINUX = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-linux-x86_64.tar.gz -tar xzvf elastic-agent-${kibanaVersion}-linux-x86_64.tar.gz -sudo ./elastic-agent install`; - - const STANDALONE_RUN_INSTRUCTIONS_MAC = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-darwin-x86_64.tar.gz -tar xzvf elastic-agent-${kibanaVersion}-darwin-x86_64.tar.gz -sudo ./elastic-agent install`; - - const STANDALONE_RUN_INSTRUCTIONS_WINDOWS = `wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-windows-x86_64.zip -OutFile elastic-agent-${kibanaVersion}-windows-x86_64.zip -Expand-Archive .\\elastic-agent-${kibanaVersion}-windows-x86_64.zip -.\\elastic-agent.exe install`; - - const linuxDebCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-amd64.deb -sudo dpkg -i elastic-agent-${kibanaVersion}-amd64.deb \nsudo systemctl enable elastic-agent \nsudo systemctl start elastic-agent`; - - const linuxRpmCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-x86_64.rpm -sudo rpm -vi elastic-agent-${kibanaVersion}-x86_64.rpm \nsudo systemctl enable elastic-agent \nsudo systemctl start elastic-agent`; - - const linuxCommand = - isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS_LINUX; - const macCommand = - isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS_MAC; - const windowsCommand = - isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS_WINDOWS; - - const { docLinks } = useStartServices(); - - useEffect(() => { - async function fetchFullPolicy() { - try { - if (!selectedPolicyId) { - return; - } - let query = { standalone: true, kubernetes: false }; - if (isK8s === 'IS_KUBERNETES') { - query = { standalone: true, kubernetes: true }; - } - const res = await sendGetOneAgentPolicyFull(selectedPolicyId, query); - if (res.error) { - throw res.error; - } - - if (!res.data) { - throw new Error('No data while fetching full agent policy'); - } - setFullAgentPolicy(res.data.item); - } catch (error) { - notifications.toasts.addError(error, { - title: 'Error', - }); - } - } - if (isK8s !== 'IS_LOADING') { - fetchFullPolicy(); - } - }, [selectedPolicyId, notifications.toasts, isK8s, core.http.basePath]); - - useEffect(() => { - if (isK8s === 'IS_KUBERNETES') { - if (typeof fullAgentPolicy === 'object') { - return; - } - setYaml(fullAgentPolicy); - } else { - if (typeof fullAgentPolicy === 'string') { - return; - } - setYaml(fullAgentPolicyToYaml(fullAgentPolicy, safeDump)); - } - }, [fullAgentPolicy, isK8s]); - - const policyMsg = - isK8s === 'IS_KUBERNETES' ? ( - ES_USERNAME, - ESPasswordVariable: ES_PASSWORD, - }} - /> - ) : ( - elastic-agent.yml, - ESUsernameVariable: ES_USERNAME, - ESPasswordVariable: ES_PASSWORD, - outputSection: outputs, - }} - /> - ); - - let downloadLink = ''; - if (selectedPolicyId) { - downloadLink = - isK8s === 'IS_KUBERNETES' - ? core.http.basePath.prepend( - `${agentPolicyRouteService.getInfoFullDownloadPath( - selectedPolicyId - )}?kubernetes=true&standalone=true` - ) - : core.http.basePath.prepend( - `${agentPolicyRouteService.getInfoFullDownloadPath(selectedPolicyId)}?standalone=true` - ); - } - - const downloadMsg = - isK8s === 'IS_KUBERNETES' ? ( - - ) : ( - - ); - - const steps = [ - !agentPolicy - ? AgentPolicySelectionStep({ - agentPolicies, - setSelectedPolicyId, - excludeFleetServer: true, - refreshAgentPolicies, - }) - : undefined, - DownloadStep(false), - { - title: i18n.translate('xpack.fleet.agentEnrollment.stepConfigureAgentTitle', { - defaultMessage: 'Configure the agent', - }), - children: ( - <> - - <>{policyMsg} - - - - - {(copy) => ( - - - - )} - - - - - <>{downloadMsg} - - - - - - {yaml} - - - - ), - }, - { - title: i18n.translate('xpack.fleet.agentEnrollment.stepRunAgentTitle', { - defaultMessage: 'Start the agent', - }), - children: ( - - ), - }, - { - title: i18n.translate('xpack.fleet.agentEnrollment.stepCheckForDataTitle', { - defaultMessage: 'Check for data', - }), - children: ( - <> - - - - - ), - }} - /> - - - ), - }, - ].filter(Boolean) as EuiContainedStepProps[]; - - return ( - <> - - - - - - - ); - } -); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_enrollment_confirmation_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_enrollment_confirmation_step.tsx new file mode 100644 index 0000000000000..743bf1a7fa578 --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_enrollment_confirmation_step.tsx @@ -0,0 +1,41 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; + +import { ConfirmAgentEnrollment } from '../confirm_agent_enrollment'; + +export const AgentEnrollmentConfirmationStep = ({ + selectedPolicyId, + troubleshootLink, + onClickViewAgents, + agentCount, +}: { + selectedPolicyId?: string; + troubleshootLink: string; + onClickViewAgents: () => void; + agentCount: number; +}): EuiContainedStepProps => { + return { + title: i18n.translate('xpack.fleet.agentEnrollment.stepAgentEnrollmentConfirmation', { + defaultMessage: 'Confirm agent Enrollment', + }), + children: ( + + ), + status: !agentCount ? 'incomplete' : 'complete', + }; +}; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_enrollment_key_selection_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_enrollment_key_selection_step.tsx new file mode 100644 index 0000000000000..bc67e834f1d22 --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_enrollment_key_selection_step.tsx @@ -0,0 +1,53 @@ +/* + * 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 { EuiText, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; + +import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; + +import type { AgentPolicy } from '../../../types'; + +import { AdvancedAgentAuthenticationSettings } from '../advanced_agent_authentication_settings'; + +export const AgentEnrollmentKeySelectionStep = ({ + selectedPolicy, + selectedApiKeyId, + setSelectedAPIKeyId, +}: { + selectedPolicy?: AgentPolicy; + selectedApiKeyId?: string; + setSelectedAPIKeyId: (key?: string) => void; +}): EuiContainedStepProps => { + return { + title: i18n.translate('xpack.fleet.agentEnrollment.stepConfigurePolicyAuthenticationTitle', { + defaultMessage: 'Select enrollment token', + }), + children: ( + <> + + {selectedPolicy?.name}, + }} + /> + + + + + ), + }; +}; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_policy_selection_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_policy_selection_step.tsx new file mode 100644 index 0000000000000..c26bfd3f0e2b8 --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_policy_selection_step.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 React from 'react'; + +import { i18n } from '@kbn/i18n'; + +import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; + +import type { AgentPolicy } from '../../../types'; + +import { SelectCreateAgentPolicy } from '../agent_policy_select_create'; + +export const AgentPolicySelectionStep = ({ + agentPolicies, + selectedPolicy, + setSelectedPolicyId, + selectedApiKeyId, + setSelectedAPIKeyId, + excludeFleetServer, + refreshAgentPolicies, +}: { + agentPolicies: AgentPolicy[]; + selectedPolicy?: AgentPolicy; + setSelectedPolicyId: (agentPolicyId?: string) => void; + selectedApiKeyId?: string; + setSelectedAPIKeyId?: (key?: string) => void; + excludeFleetServer?: boolean; + refreshAgentPolicies: () => void; +}): EuiContainedStepProps => { + return { + title: i18n.translate('xpack.fleet.agentEnrollment.stepChooseAgentPolicyTitle', { + defaultMessage: 'What type of host are you adding?', + }), + children: ( + <> + + + ), + }; +}; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx new file mode 100644 index 0000000000000..cae508299f259 --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx @@ -0,0 +1,369 @@ +/* + * 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, { useState, useMemo, useEffect } from 'react'; + +import { EuiSteps } from '@elastic/eui'; +import { safeDump } from 'js-yaml'; + +import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; + +import type { FullAgentPolicy } from '../../../../common/types/models/agent_policy'; + +import { fullAgentPolicyToYaml, agentPolicyRouteService } from '../../../services'; + +import { StandaloneInstructions } from '../../enrollment_instructions/standalone'; + +import { + useGetOneEnrollmentAPIKey, + useStartServices, + useKibanaVersion, + sendGetOneAgentPolicyFull, +} from '../../../hooks'; + +import { + deploymentModeStep, + ServiceTokenStep, + FleetServerCommandStep, + useFleetServerInstructions, + addFleetServerHostStep, +} from '../../../applications/fleet/sections/agents/agent_requirements_page/components'; + +import type { InstructionProps } from '../types'; +import { usePollingAgentCount } from '../confirm_agent_enrollment'; + +import { + InstallationModeSelectionStep, + AgentEnrollmentKeySelectionStep, + AgentPolicySelectionStep, + InstallStandaloneAgentStep, + ConfigureStandaloneAgentStep, + AgentEnrollmentConfirmationStep, + InstallManagedAgentStep, + IncomingDataConfirmationStep, +} from '.'; + +export const StandaloneSteps: React.FunctionComponent = ({ + agentPolicy, + agentPolicies, + selectedPolicy, + setSelectedPolicyId, + refreshAgentPolicies, + mode, + setMode, + selectionType, + selectedApiKeyId, + setSelectedAPIKeyId, + isK8s, +}) => { + const core = useStartServices(); + const { notifications } = core; + const [fullAgentPolicy, setFullAgentPolicy] = useState(); + const [yaml, setYaml] = useState(''); + const kibanaVersion = useKibanaVersion(); + + let downloadLink = ''; + + if (selectedPolicy?.id) { + downloadLink = + isK8s === 'IS_KUBERNETES' + ? core.http.basePath.prepend( + `${agentPolicyRouteService.getInfoFullDownloadPath( + selectedPolicy?.id + )}?kubernetes=true&standalone=true` + ) + : core.http.basePath.prepend( + `${agentPolicyRouteService.getInfoFullDownloadPath(selectedPolicy?.id)}?standalone=true` + ); + } + + useEffect(() => { + async function fetchFullPolicy() { + try { + if (!selectedPolicy?.id) { + return; + } + let query = { standalone: true, kubernetes: false }; + if (isK8s === 'IS_KUBERNETES') { + query = { standalone: true, kubernetes: true }; + } + const res = await sendGetOneAgentPolicyFull(selectedPolicy?.id, query); + if (res.error) { + throw res.error; + } + + if (!res.data) { + throw new Error('No data while fetching full agent policy'); + } + setFullAgentPolicy(res.data.item); + } catch (error) { + notifications.toasts.addError(error, { + title: 'Error', + }); + } + } + if (isK8s !== 'IS_LOADING') { + fetchFullPolicy(); + } + }, [selectedPolicy, notifications.toasts, isK8s, core.http.basePath]); + + useEffect(() => { + if (!fullAgentPolicy) { + return; + } + if (isK8s === 'IS_KUBERNETES') { + if (typeof fullAgentPolicy === 'object') { + return; + } + setYaml(fullAgentPolicy); + } else { + if (typeof fullAgentPolicy === 'string') { + return; + } + setYaml(fullAgentPolicyToYaml(fullAgentPolicy, safeDump)); + } + }, [fullAgentPolicy, isK8s]); + + const instructionsSteps = useMemo(() => { + const standaloneInstallCommands = StandaloneInstructions(kibanaVersion, isK8s); + + const steps: EuiContainedStepProps[] = !agentPolicy + ? [ + AgentPolicySelectionStep({ + selectedPolicy, + agentPolicies, + selectedApiKeyId, + setSelectedAPIKeyId, + setSelectedPolicyId, + refreshAgentPolicies, + }), + ] + : []; + + if (selectionType === 'radio') { + steps.push(InstallationModeSelectionStep({ mode, setMode })); + } + + steps.push( + ConfigureStandaloneAgentStep({ + isK8s, + selectedPolicyId: selectedPolicy?.id, + yaml, + downloadLink, + }) + ); + + steps.push( + InstallStandaloneAgentStep({ + installCommand: standaloneInstallCommands, + isK8s, + selectedPolicyId: selectedPolicy?.id, + }) + ); + + return steps; + }, [ + kibanaVersion, + isK8s, + agentPolicy, + selectedPolicy, + agentPolicies, + selectedApiKeyId, + setSelectedAPIKeyId, + setSelectedPolicyId, + refreshAgentPolicies, + selectionType, + yaml, + downloadLink, + mode, + setMode, + ]); + + return ; +}; + +export const ManagedSteps: React.FunctionComponent = ({ + agentPolicy, + agentPolicies, + selectedPolicy, + setSelectedPolicyId, + selectedApiKeyId, + setSelectedAPIKeyId, + settings, + refreshAgentPolicies, + mode, + setMode, + selectionType, + onClickViewAgents, + isK8s, + installedPackagePolicy, +}) => { + const core = useStartServices(); + const { docLinks } = core; + const link = docLinks.links.fleet.troubleshooting; + const [agentDataConfirmed, setAgentDataConfirmed] = useState(false); + + const apiKey = useGetOneEnrollmentAPIKey(selectedApiKeyId); + const apiKeyData = apiKey?.data; + const enrolledAgentIds = usePollingAgentCount(selectedPolicy?.id || ''); + + const fleetServerHosts = useMemo(() => { + return settings?.fleet_server_hosts || []; + }, [settings]); + + const instructionsSteps = useMemo(() => { + const steps: EuiContainedStepProps[] = !agentPolicy + ? [ + AgentPolicySelectionStep({ + selectedPolicy, + agentPolicies, + selectedApiKeyId, + setSelectedAPIKeyId, + setSelectedPolicyId, + refreshAgentPolicies, + }), + ] + : [ + AgentEnrollmentKeySelectionStep({ + selectedPolicy, + selectedApiKeyId, + setSelectedAPIKeyId, + }), + ]; + + if (selectionType === 'radio') { + steps.push(InstallationModeSelectionStep({ mode, setMode })); + } + + steps.push( + InstallManagedAgentStep({ + apiKeyData, + selectedApiKeyId, + fleetServerHosts, + isK8s, + }) + ); + if (selectedApiKeyId && apiKeyData) { + steps.push( + AgentEnrollmentConfirmationStep({ + selectedPolicyId: selectedPolicy?.id, + onClickViewAgents, + troubleshootLink: link, + agentCount: enrolledAgentIds.length, + }) + ); + } + if (selectedPolicy && enrolledAgentIds.length) { + steps.push( + IncomingDataConfirmationStep({ + agentIds: enrolledAgentIds, + agentDataConfirmed, + setAgentDataConfirmed, + installedPolicy: installedPackagePolicy, + }) + ); + } + + return steps; + }, [ + agentPolicy, + selectedPolicy, + agentPolicies, + selectedApiKeyId, + setSelectedAPIKeyId, + setSelectedPolicyId, + refreshAgentPolicies, + selectionType, + apiKeyData, + fleetServerHosts, + isK8s, + mode, + setMode, + onClickViewAgents, + link, + enrolledAgentIds, + agentDataConfirmed, + installedPackagePolicy, + ]); + + return ; +}; + +export const FleetServerSteps: React.FunctionComponent = ({ + agentPolicy, + agentPolicies, + selectedPolicy, + setSelectedPolicyId, + refreshAgentPolicies, +}) => { + const [selectedApiKeyId, setSelectedAPIKeyId] = useState(); + + const apiKey = useGetOneEnrollmentAPIKey(selectedApiKeyId); + const apiKeyData = apiKey?.data; + const fleetServerInstructions = useFleetServerInstructions(apiKeyData?.item?.policy_id); + + const fleetServerSteps = useMemo(() => { + const { + serviceToken, + getServiceToken, + isLoadingServiceToken, + installCommand: managedInstallCommands, + platform, + setPlatform, + deploymentMode, + setDeploymentMode, + addFleetServerHost, + } = fleetServerInstructions; + + return [ + deploymentModeStep({ deploymentMode, setDeploymentMode }), + addFleetServerHostStep({ addFleetServerHost }), + ServiceTokenStep({ serviceToken, getServiceToken, isLoadingServiceToken }), + FleetServerCommandStep({ + serviceToken, + installCommand: managedInstallCommands, + platform, + setPlatform, + }), + ]; + }, [fleetServerInstructions]); + + const instructionsSteps = useMemo(() => { + const steps: EuiContainedStepProps[] = !agentPolicy + ? [ + AgentPolicySelectionStep({ + selectedPolicy, + agentPolicies, + selectedApiKeyId, + setSelectedAPIKeyId, + setSelectedPolicyId, + refreshAgentPolicies, + }), + ] + : [ + AgentEnrollmentKeySelectionStep({ + selectedPolicy, + selectedApiKeyId, + setSelectedAPIKeyId, + }), + ]; + + steps.push(...fleetServerSteps); + + return steps; + }, [ + agentPolicy, + selectedPolicy, + agentPolicies, + selectedApiKeyId, + setSelectedPolicyId, + refreshAgentPolicies, + fleetServerSteps, + ]); + + return ; +}; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/configure_standalone_agent_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/configure_standalone_agent_step.tsx new file mode 100644 index 0000000000000..c0b64a88e19cc --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/configure_standalone_agent_step.tsx @@ -0,0 +1,116 @@ +/* + * 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 { + EuiText, + EuiButton, + EuiSpacer, + EuiCode, + EuiFlexGroup, + EuiFlexItem, + EuiCopy, + EuiCodeBlock, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; + +import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; + +import type { K8sMode } from '../types'; + +export const ConfigureStandaloneAgentStep = ({ + isK8s, + selectedPolicyId, + yaml, + downloadLink, +}: { + isK8s?: K8sMode; + selectedPolicyId?: string; + yaml: string; + downloadLink: string; +}): EuiContainedStepProps => { + const policyMsg = + isK8s === 'IS_KUBERNETES' ? ( + ES_USERNAME, + ESPasswordVariable: ES_PASSWORD, + }} + /> + ) : ( + elastic-agent.yml, + ESUsernameVariable: ES_USERNAME, + ESPasswordVariable: ES_PASSWORD, + outputSection: outputs, + }} + /> + ); + + const downloadMsg = + isK8s === 'IS_KUBERNETES' ? ( + + ) : ( + + ); + return { + title: i18n.translate('xpack.fleet.agentEnrollment.stepConfigureAgentTitle', { + defaultMessage: 'Configure the agent', + }), + children: ( + <> + {!yaml ? null : ( + + <>{policyMsg} + + + + + {(copy) => ( + + + + )} + + + + + <>{downloadMsg} + + + + + + {yaml} + + + )} + + ), + status: !yaml ? 'loading' : 'incomplete', + }; +}; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/download_step.tsx similarity index 67% rename from x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx rename to x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/download_step.tsx index 54c449f74cc60..5004fc0a6444e 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/download_step.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { EuiText, EuiButton, @@ -22,21 +22,19 @@ import semverMajor from 'semver/functions/major'; import semverMinor from 'semver/functions/minor'; import semverPatch from 'semver/functions/patch'; -import type { AgentPolicy } from '../../types'; -import { useGetSettings, useKibanaVersion, useStartServices } from '../../hooks'; +import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; -import { agentPolicyRouteService } from '../../../common'; +import { useGetSettings, useKibanaVersion, useStartServices } from '../../../hooks'; -import { sendGetK8sManifest } from '../../hooks/use_request/k8s'; +import { agentPolicyRouteService } from '../../../../common'; -import { AdvancedAgentAuthenticationSettings } from './advanced_agent_authentication_settings'; -import { SelectCreateAgentPolicy } from './agent_policy_select_create'; +import { sendGetK8sManifest } from '../../../hooks/use_request/k8s'; export const DownloadStep = ( hasFleetServer: boolean, isK8s?: string, enrollmentAPIKey?: string -) => { +): EuiContainedStepProps => { const kibanaVersion = useKibanaVersion(); const core = useStartServices(); const settings = useGetSettings(); @@ -49,6 +47,7 @@ export const DownloadStep = ( const [yaml, setYaml] = useState(); const [fleetServer, setFleetServer] = useState(); + useEffect(() => { async function fetchK8sManifest() { try { @@ -132,6 +131,7 @@ export const DownloadStep = ( ) : ( '' ); + const k8sCopyYaml = isK8s === 'IS_KUBERNETES' ? ( @@ -147,6 +147,7 @@ export const DownloadStep = ( ) : ( '' ); + const k8sYaml = isK8s === 'IS_KUBERNETES' ? ( @@ -202,96 +203,3 @@ export const DownloadStep = ( ), }; }; - -export const AgentPolicySelectionStep = ({ - agentPolicies, - setSelectedPolicyId, - selectedApiKeyId, - setSelectedAPIKeyId, - excludeFleetServer, - refreshAgentPolicies, -}: { - agentPolicies: AgentPolicy[]; - setSelectedPolicyId?: (policyId?: string) => void; - selectedApiKeyId?: string; - setSelectedAPIKeyId?: (key?: string) => void; - excludeFleetServer?: boolean; - refreshAgentPolicies: () => void; -}) => { - // storing the created agent policy id as the child component is being recreated - const [policyId, setPolicyId] = useState(undefined); - const regularAgentPolicies = useMemo(() => { - return agentPolicies.filter( - (policy) => - policy && !policy.is_managed && (!excludeFleetServer || !policy.is_default_fleet_server) - ); - }, [agentPolicies, excludeFleetServer]); - - const onAgentPolicyChange = useCallback( - async (key?: string, policy?: AgentPolicy) => { - if (policy) { - refreshAgentPolicies(); - } - if (setSelectedPolicyId) { - setSelectedPolicyId(key); - setPolicyId(key); - } - }, - [setSelectedPolicyId, refreshAgentPolicies] - ); - - return { - title: i18n.translate('xpack.fleet.agentEnrollment.stepChooseAgentPolicyTitle', { - defaultMessage: 'What type of host are you adding?', - }), - children: ( - <> - - - ), - }; -}; - -export const AgentEnrollmentKeySelectionStep = ({ - agentPolicy, - selectedApiKeyId, - setSelectedAPIKeyId, -}: { - agentPolicy: AgentPolicy; - selectedApiKeyId?: string; - setSelectedAPIKeyId: (key?: string) => void; -}) => { - return { - title: i18n.translate('xpack.fleet.agentEnrollment.stepConfigurePolicyAuthenticationTitle', { - defaultMessage: 'Select enrollment token', - }), - children: ( - <> - - {agentPolicy.name}, - }} - /> - - - - - ), - }; -}; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/incoming_data_confirmation_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/incoming_data_confirmation_step.tsx new file mode 100644 index 0000000000000..76cd1c07f4630 --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/incoming_data_confirmation_step.tsx @@ -0,0 +1,47 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; + +import type { InstalledIntegrationPolicy } from '../use_get_agent_incoming_data'; + +import { ConfirmIncomingData } from '../confirm_incoming_data'; + +export const IncomingDataConfirmationStep = ({ + agentIds, + installedPolicy, + agentDataConfirmed, + setAgentDataConfirmed, +}: { + agentIds: string[]; + installedPolicy?: InstalledIntegrationPolicy; + agentDataConfirmed: boolean; + setAgentDataConfirmed: (v: boolean) => void; +}): EuiContainedStepProps => { + return { + title: !agentDataConfirmed + ? i18n.translate('xpack.fleet.agentEnrollment.stepConfirmIncomingData', { + defaultMessage: 'Confirm incoming data', + }) + : i18n.translate('xpack.fleet.agentEnrollment.stepConfirmIncomingData.completed', { + defaultMessage: 'Incoming data confirmed', + }), + children: ( + + ), + status: !agentDataConfirmed ? 'loading' : 'complete', + }; +}; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/index.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/index.tsx new file mode 100644 index 0000000000000..53af0e66f7811 --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/index.tsx @@ -0,0 +1,17 @@ +/* + * 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 * from './agent_enrollment_confirmation_step'; +export * from './agent_enrollment_key_selection_step'; +export * from './agent_policy_selection_step'; +export * from './configure_standalone_agent_step'; +export * from './download_step'; +export * from './incoming_data_confirmation_step'; +export * from './install_managed_agent_step'; +export * from './install_standalone_agent_step'; +export * from './installation_mode_selection_step'; +export * from './compute_steps'; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_managed_agent_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_managed_agent_step.tsx new file mode 100644 index 0000000000000..03e8c305a6fcb --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_managed_agent_step.tsx @@ -0,0 +1,41 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; + +import type { GetOneEnrollmentAPIKeyResponse } from '../../../../common/types/rest_spec/enrollment_api_key'; + +import { ManualInstructions } from '../../enrollment_instructions'; + +export const InstallManagedAgentStep = ({ + selectedApiKeyId, + apiKeyData, + fleetServerHosts, + isK8s, +}: { + fleetServerHosts: string[]; + selectedApiKeyId?: string; + apiKeyData?: GetOneEnrollmentAPIKeyResponse | null; + isK8s?: string; +}): EuiContainedStepProps => { + return { + title: i18n.translate('xpack.fleet.agentEnrollment.stepEnrollAndRunAgentTitle', { + defaultMessage: 'Install Elastic Agent on your host', + }), + children: selectedApiKeyId && apiKeyData && ( + + ), + }; +}; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_standalone_agent_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_standalone_agent_step.tsx new file mode 100644 index 0000000000000..f1c6ce88233db --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_standalone_agent_step.tsx @@ -0,0 +1,47 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; + +import type { CommandsByPlatform } from '../../../applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils'; +import { PlatformSelector } from '../../enrollment_instructions/manual/platform_selector'; + +import { InstallationMessage } from '../installation_message'; + +import type { K8sMode } from '../types'; + +export const InstallStandaloneAgentStep = ({ + installCommand, + isK8s, + selectedPolicyId, +}: { + installCommand: CommandsByPlatform; + isK8s?: K8sMode; + selectedPolicyId?: string; +}): EuiContainedStepProps => { + return { + title: i18n.translate('xpack.fleet.agentEnrollment.stepEnrollAndRunAgentTitle', { + defaultMessage: 'Install Elastic Agent on your host', + }), + children: ( + <> + + + + ), + }; +}; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/installation_mode_selection_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/installation_mode_selection_step.tsx new file mode 100644 index 0000000000000..3864599fb6a94 --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/installation_mode_selection_step.tsx @@ -0,0 +1,88 @@ +/* + * 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 { EuiRadioGroup } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; + +import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; + +import type { FlyoutMode } from '../types'; + +export const InstallationModeSelectionStep = ({ + mode, + setMode, +}: { + mode: FlyoutMode; + setMode: (v: FlyoutMode) => void; +}): EuiContainedStepProps => { + // radio id has to be unique so that the component works even if appears twice in DOM + const radioSuffix = 'installation_mode_agent_selection'; + + const onChangeCallback = (v: string) => { + const value = v.split('_')[0]; + if (value === 'managed' || value === 'standalone') { + setMode(value); + } + }; + + return { + title: i18n.translate('xpack.fleet.agentEnrollment.stepInstallType', { + defaultMessage: 'Enroll in Fleet?', + }), + children: ( + + + + ), + }} + /> + ), + }, + { + id: `standalone_${radioSuffix}`, + label: ( + + + + ), + }} + /> + ), + }, + ]} + idSelected={`${mode}_${radioSuffix}`} + onChange={onChangeCallback} + name={`radio group ${radioSuffix}`} + /> + ), + }; +}; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts index edb34e876e7c8..0c2d9361aa3d6 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts @@ -5,35 +5,50 @@ * 2.0. */ -import type { EuiStepProps } from '@elastic/eui'; - import type { AgentPolicy, Settings } from '../../types'; +import type { InstalledIntegrationPolicy } from './use_get_agent_incoming_data'; + +export type K8sMode = 'IS_LOADING' | 'IS_KUBERNETES' | 'IS_NOT_KUBERNETES'; +export type FlyoutMode = 'managed' | 'standalone'; +export type SelectionType = 'tabs' | 'radio'; + export interface BaseProps { /** * The user selected policy to be used. If this value is `undefined` a value must be provided for `agentPolicies`. */ agentPolicy?: AgentPolicy; - /** - * There is a step in the agent enrollment process that allows users to see the data from an integration represented in the UI - * in some way. This is an area for consumers to render a button and text explaining how data can be viewed. - */ - viewDataStep?: EuiStepProps; - settings?: Settings; - setSelectedPolicyId?: (policyId?: string) => void; + isFleetServerPolicySelected?: boolean; - policyId?: string; + isK8s?: K8sMode; - isFleetServerPolicySelected?: boolean; + /** + * There is a step in the agent enrollment process that allows users to see the data from an integration represented in the UI + * in some way. This is an area for consumers to render a button and text explaining how data can be viewed. + */ + isIntegrationFlow?: boolean; + installedPackagePolicy?: InstalledIntegrationPolicy; +} - isK8s?: string; +export interface FlyOutProps extends BaseProps { + onClose: () => void; + defaultMode?: FlyoutMode; } export interface InstructionProps extends BaseProps { agentPolicies: AgentPolicy[]; + selectedPolicy: AgentPolicy | undefined; + setSelectedPolicyId: (policyId?: string) => void; refreshAgentPolicies: () => void; isLoadingAgentPolicies?: boolean; + onClickViewAgents: () => void; + mode: FlyoutMode; + setMode: (v: FlyoutMode) => void; + selectionType: SelectionType; + setSelectionType: (type: SelectionType) => void; + selectedApiKeyId?: string; + setSelectedAPIKeyId: (key?: string) => void; } diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/use_get_agent_incoming_data.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/use_get_agent_incoming_data.tsx new file mode 100644 index 0000000000000..4922a7d10c78c --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/use_get_agent_incoming_data.tsx @@ -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 { useEffect, useState, useMemo, useRef } from 'react'; +import { i18n } from '@kbn/i18n'; + +import type { IncomingDataList } from '../../../common/types/rest_spec/agent'; + +import { sendGetAgentIncomingData, useLink } from '../../hooks/index'; + +export interface InstalledIntegrationPolicy { + name: string; + version: string; +} + +export const useGetAgentIncomingData = ( + incomingData: IncomingDataList[], + installedPolicy?: InstalledIntegrationPolicy +) => { + const enrolledAgents = useMemo(() => incomingData.length, [incomingData.length]); + const numAgentsWithData = useMemo( + () => + incomingData.reduce((acc, curr) => { + const agentData = Object.values(curr)[0]; + return !!agentData.data ? acc + 1 : acc; + }, 0), + [incomingData] + ); + const { getAbsolutePath, getHref } = useLink(); + + let href; + let text; + let message; + + if (!installedPolicy) { + href = ''; + text = ''; + message = ''; + } + + if (installedPolicy?.name === 'apm') { + href = getAbsolutePath('/app/home#/tutorial/apm'); + text = i18n.translate('xpack.fleet.confirmIncomingData.installApmAgentButtonText', { + defaultMessage: 'Install APM Agent', + }); + message = i18n.translate('xpack.fleet.confirmIncomingData.APMsubtitle', { + defaultMessage: + 'Next, install APM agents on your hosts to collect data from your applications and services.', + }); + } else { + href = getHref('integration_details_assets', { + pkgkey: `${installedPolicy?.name}-${installedPolicy?.version}`, + }); + text = i18n.translate('xpack.fleet.confirmIncomingData.viewDataAssetsButtonText', { + defaultMessage: 'View assets', + }); + message = i18n.translate('xpack.fleet.confirmIncomingData.subtitle', { + defaultMessage: + 'Next, analyze your data using our integration assets such as curated views, dashboards and more.', + }); + } + const linkButton = { href, text }; + + return { + enrolledAgents, + numAgentsWithData, + linkButton, + message, + }; +}; + +/** + * Hook for polling incoming data for the selected agent policy. + * @param agentIds + * @returns incomingData, isLoading + */ +const POLLING_INTERVAL_MS = 5 * 1000; // 5 sec + +export const usePollingIncomingData = (agentsIds: string[]) => { + const timeout = useRef(undefined); + const [incomingData, setIncomingData] = useState([]); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + let isAborted = false; + + const poll = () => { + timeout.current = window.setTimeout(async () => { + const { data } = await sendGetAgentIncomingData({ agentsIds }); + + if (data?.items) { + // filter out agents that have `data = false` and keep polling + const filtered = data?.items.filter((item) => { + const key = Object.keys(item)[0]; + return item[key].data === true; + }); + + if (filtered.length > 0) { + setIncomingData(filtered); + setIsLoading(false); + } + } + if (!isAborted) { + poll(); + } + }, POLLING_INTERVAL_MS); + }; + + poll(); + if (isAborted || incomingData.length > 0) clearTimeout(timeout.current); + + return () => { + isAborted = true; + }; + }, [agentsIds, incomingData]); + + return { incomingData, isLoading }; +}; diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx b/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx index 020ca9dc053a7..077f9db0b05a2 100644 --- a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx +++ b/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx @@ -7,15 +7,16 @@ import React from 'react'; -import { useStartServices, useKibanaVersion } from '../../../hooks'; +import { useKibanaVersion } from '../../../hooks'; import type { EnrollmentAPIKey } from '../../../types'; +import { InstallationMessage } from '../../agent_enrollment_flyout/installation_message'; + import { PlatformSelector } from './platform_selector'; interface Props { fleetServerHosts: string[]; apiKey: EnrollmentAPIKey; - policyId: string | undefined; isK8s: string | undefined; } @@ -26,10 +27,8 @@ function getfleetServerHostsEnrollArgs(apiKey: EnrollmentAPIKey, fleetServerHost export const ManualInstructions: React.FunctionComponent = ({ apiKey, fleetServerHosts, - policyId, isK8s, }) => { - const { docLinks } = useStartServices(); const enrollArgs = getfleetServerHostsEnrollArgs(apiKey, fleetServerHosts); const kibanaVersion = useKibanaVersion(); @@ -57,14 +56,16 @@ sudo rpm -vi elastic-agent-${kibanaVersion}-x86_64.rpm sudo elastic-agent enroll ${enrollArgs} \nsudo systemctl enable elastic-agent \nsudo systemctl start elastic-agent`; return ( - + <> + + + ); }; diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/platform_selector.tsx b/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/platform_selector.tsx index 7fc1c827596e9..9551d9a7eae87 100644 --- a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/platform_selector.tsx +++ b/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/platform_selector.tsx @@ -7,14 +7,7 @@ import React from 'react'; import styled from 'styled-components'; -import { - EuiText, - EuiSpacer, - EuiLink, - EuiCodeBlock, - EuiButtonGroup, - EuiCallOut, -} from '@elastic/eui'; +import { EuiText, EuiSpacer, EuiCodeBlock, EuiButtonGroup, EuiCallOut } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; @@ -27,7 +20,6 @@ interface Props { windowsCommand: string; linuxDebCommand: string; linuxRpmCommand: string; - troubleshootLink: string; isK8s: boolean; } @@ -44,7 +36,6 @@ export const PlatformSelector: React.FunctionComponent = ({ windowsCommand, linuxDebCommand, linuxRpmCommand, - troubleshootLink, isK8s, }) => { const { platform, setPlatform } = usePlatform(); @@ -127,23 +118,6 @@ export const PlatformSelector: React.FunctionComponent = ({ )} )} - - - - - - ), - }} - /> - ); }; diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx b/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx new file mode 100644 index 0000000000000..db36c16934e4b --- /dev/null +++ b/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx @@ -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 type { CommandsByPlatform } from '../../../applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils'; +import type { K8sMode } from '../../../components/agent_enrollment_flyout/types'; + +export const StandaloneInstructions = ( + kibanaVersion: string, + isK8s?: K8sMode +): CommandsByPlatform => { + const KUBERNETES_RUN_INSTRUCTIONS = 'kubectl apply -f elastic-agent-standalone-kubernetes.yaml'; + + const STANDALONE_RUN_INSTRUCTIONS_LINUX = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-linux-x86_64.tar.gz +tar xzvf elastic-agent-${kibanaVersion}-linux-x86_64.tar.gz +cd elastic-agent-${kibanaVersion}-linux-x86_64 +sudo ./elastic-agent install`; + + const STANDALONE_RUN_INSTRUCTIONS_MAC = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-darwin-x86_64.tar.gz +tar xzvf elastic-agent-${kibanaVersion}-darwin-x86_64.tar.gz +cd elastic-agent-${kibanaVersion}-darwin-x86_64 +sudo ./elastic-agent install`; + + const STANDALONE_RUN_INSTRUCTIONS_WINDOWS = `wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-windows-x86_64.zip -OutFile elastic-agent-${kibanaVersion}-windows-x86_64.zip +Expand-Archive .\elastic-agent-${kibanaVersion}-windows-x86_64.zip +cd elastic-agent-${kibanaVersion}-windows-x86_64 +.\\elastic-agent.exe install`; + + const linuxDebCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-amd64.deb + sudo dpkg -i elastic-agent-${kibanaVersion}-amd64.deb \nsudo systemctl enable elastic-agent \nsudo systemctl start elastic-agent`; + + const linuxRpmCommand = `curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-x86_64.rpm + sudo rpm -vi elastic-agent-${kibanaVersion}-x86_64.rpm \nsudo systemctl enable elastic-agent \nsudo systemctl start elastic-agent`; + + const linuxCommand = + isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS_LINUX; + const macCommand = + isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS_MAC; + const windowsCommand = + isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS_WINDOWS; + + return { + linux: linuxCommand, + mac: macCommand, + windows: windowsCommand, + deb: linuxDebCommand, + rpm: linuxRpmCommand, + }; +}; diff --git a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx index 3338405c0b6c1..42b907cc282f4 100644 --- a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx +++ b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx @@ -7,7 +7,6 @@ import React, { useMemo, useState } from 'react'; import { EuiContextMenuItem, EuiPortal } from '@elastic/eui'; -import type { EuiStepProps } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import type { AgentPolicy, InMemoryPackagePolicy } from '../types'; @@ -22,14 +21,12 @@ import { PackagePolicyDeleteProvider } from './package_policy_delete_provider'; export const PackagePolicyActionsMenu: React.FunctionComponent<{ agentPolicy: AgentPolicy; packagePolicy: InMemoryPackagePolicy; - viewDataStep?: EuiStepProps; showAddAgent?: boolean; defaultIsOpen?: boolean; upgradePackagePolicyHref: string; }> = ({ agentPolicy, packagePolicy, - viewDataStep, showAddAgent, upgradePackagePolicyHref, defaultIsOpen = false, @@ -43,7 +40,6 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{ const onEnrollmentFlyoutClose = useMemo(() => { return () => setIsEnrollmentFlyoutOpen(false); }, []); - const menuItems = [ // FIXME: implement View package policy action // )} diff --git a/x-pack/plugins/fleet/public/hooks/index.ts b/x-pack/plugins/fleet/public/hooks/index.ts index c5dcdd78b9bb9..5c995131396b4 100644 --- a/x-pack/plugins/fleet/public/hooks/index.ts +++ b/x-pack/plugins/fleet/public/hooks/index.ts @@ -27,4 +27,3 @@ export * from './use_platform'; export * from './use_agent_policy_refresh'; export * from './use_package_installations'; export * from './use_agent_enrollment_flyout_data'; -export * from './use_get_agent_incoming_data'; diff --git a/x-pack/plugins/fleet/public/hooks/use_get_agent_incoming_data.tsx b/x-pack/plugins/fleet/public/hooks/use_get_agent_incoming_data.tsx deleted file mode 100644 index a14dbd30aef45..0000000000000 --- a/x-pack/plugins/fleet/public/hooks/use_get_agent_incoming_data.tsx +++ /dev/null @@ -1,78 +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 { useEffect, useState, useMemo } from 'react'; -import { i18n } from '@kbn/i18n'; - -import type { IncomingDataList } from '../../common/types/rest_spec/agent'; - -import { sendGetAgentIncomingData, useLink } from './index'; - -export interface InstalledIntegrationPolicy { - name: string; - version: string; -} - -export const useGetAgentIncomingData = ( - agentsIds: string[], - installedPolicy?: InstalledIntegrationPolicy -) => { - const [isLoading, setIsLoading] = useState(true); - const [incomingData, setIncomingData] = useState([]); - - useEffect(() => { - const getIncomingData = async () => { - const { data } = await sendGetAgentIncomingData({ agentsIds }); - if (data?.items) { - setIncomingData(data?.items); - setIsLoading(false); - } - }; - if (agentsIds) { - getIncomingData(); - } - }, [agentsIds]); - - const enrolledAgents = useMemo(() => incomingData.length, [incomingData.length]); - const numAgentsWithData = useMemo( - () => - incomingData.reduce((acc, curr) => { - const agentData = Object.values(curr)[0]; - return !!agentData.data ? acc + 1 : acc; - }, 0), - [incomingData] - ); - const { getAbsolutePath, getHref } = useLink(); - - let href; - let text; - if (!installedPolicy) { - href = ''; - text = ''; - } - - if (installedPolicy?.name === 'apm') { - href = getAbsolutePath('/app/home#/tutorial/apm'); - text = i18n.translate('xpack.fleet.confirmIncomingData.installApmAgentButtonText', { - defaultMessage: 'Install APM Agent', - }); - } else { - href = getHref('integration_details_assets', { - pkgkey: `${installedPolicy?.name}-${installedPolicy?.version}`, - }); - text = i18n.translate('xpack.fleet.confirmIncomingData.viewDataAssetsButtonText', { - defaultMessage: 'View assets', - }); - } - const linkButton = { href, text }; - - return { - enrolledAgents, - numAgentsWithData, - isLoading, - linkButton, - }; -}; diff --git a/x-pack/plugins/fleet/public/hooks/use_platform.tsx b/x-pack/plugins/fleet/public/hooks/use_platform.tsx index 286a685e89c84..6f8a41aba59e8 100644 --- a/x-pack/plugins/fleet/public/hooks/use_platform.tsx +++ b/x-pack/plugins/fleet/public/hooks/use_platform.tsx @@ -18,7 +18,7 @@ export const PLATFORM_OPTIONS: Array<{ { id: 'linux', label: i18n.translate('xpack.fleet.enrollmentInstructions.platformButtons.linux', { - defaultMessage: 'Linux', + defaultMessage: 'Linux Tar', }), 'data-test-subj': 'platformTypeLinux', }, diff --git a/x-pack/plugins/fleet/server/services/agents/status.ts b/x-pack/plugins/fleet/server/services/agents/status.ts index 828a3b1622cef..90113d1d177ef 100644 --- a/x-pack/plugins/fleet/server/services/agents/status.ts +++ b/x-pack/plugins/fleet/server/services/agents/status.ts @@ -144,7 +144,7 @@ export async function getIncomingDataByAgentsId( agent_ids: { terms: { field: 'agent.id', - size: 10, + size: agentsIds.length, }, }, }, diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index b6cc88c5a8c92..687339109cbb0 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -9843,19 +9843,14 @@ "xpack.fleet.agentEnrollment.downloadLink": "Accéder à la page de téléchargement", "xpack.fleet.agentEnrollment.downloadPolicyButton": "Télécharger la stratégie", "xpack.fleet.agentEnrollment.downloadUseLinuxInstaller": "Utilisateurs de Linux : nous vous recommandons d'utiliser les programmes d'installation sur (RPM/DEB), car ils permettent la mise à niveau de votre agent dans Fleet.", - "xpack.fleet.agentEnrollment.enrollFleetTabLabel": "Enregistrer dans Fleet", - "xpack.fleet.agentEnrollment.enrollStandaloneTabLabel": "Exécuter de façon autonome", "xpack.fleet.agentEnrollment.fleetSettingsLink": "Paramètres de Fleet", "xpack.fleet.agentEnrollment.flyoutTitle": "Ajouter un agent", - "xpack.fleet.agentEnrollment.goToDataStreamsLink": "flux de données", "xpack.fleet.agentEnrollment.managedDescription": "L'enregistrement d'un agent Elastic Agent dans Fleet permet de centraliser la gestion de ce dernier tout en déployant automatiquement les mises à jour.", "xpack.fleet.agentEnrollment.missingFleetHostCalloutText": "L'enregistrement d'agents dans Fleet nécessite l'URL de l'hôte de votre serveur Fleet. Vous pouvez ajouter ces informations dans Paramètres de Fleet. Pour en savoir plus, consultez {link}.", "xpack.fleet.agentEnrollment.missingFleetHostCalloutTitle": "URL de l'hôte du serveur Fleet manquante", "xpack.fleet.agentEnrollment.missingFleetHostGuideLink": "Guide de l'utilisateur de Fleet", "xpack.fleet.agentEnrollment.setUpAgentsLink": "configurer la gestion centralisée des agents Elastic", "xpack.fleet.agentEnrollment.standaloneDescription": "Exécutez un agent Elastic Agent de façon autonome pour le configurer et le mettre à jour manuellement sur l'hôte sur lequel il est installé.", - "xpack.fleet.agentEnrollment.stepCheckForDataDescription": "L'agent devrait commencer à envoyer des données. Accédez à {link} pour consulter vos données.", - "xpack.fleet.agentEnrollment.stepCheckForDataTitle": "Rechercher des données", "xpack.fleet.agentEnrollment.stepChooseAgentPolicyTitle": "Sélectionner une stratégie d'agent", "xpack.fleet.agentEnrollment.stepConfigureAgentDescription": "Copiez cette stratégie dans le fichier {fileName} de l'hôte sur lequel l'agent Elastic Agent est installé. Modifiez {ESUsernameVariable} et {ESPasswordVariable} dans la section {outputSection} du fichier {fileName} pour utiliser vos identifiants de connexion Elasticsearch.", "xpack.fleet.agentEnrollment.stepConfigureAgentTitle": "Configurer l'agent", @@ -9863,9 +9858,6 @@ "xpack.fleet.agentEnrollment.stepDownloadAgentTitle": "Télécharger l'agent Elastic Agent sur votre hôte", "xpack.fleet.agentEnrollment.stepEnrollAndRunAgentTitle": "Enregistrer et démarrer l'agent Elastic Agent", "xpack.fleet.agentEnrollment.stepRunAgentDescription": "Depuis le répertoire des agents, exécutez cette commande pour installer, enregistrer et démarrer un agent Elastic Agent. Vous pouvez réutiliser cette commande pour configurer des agents sur plusieurs hôtes. Cette action nécessite de disposer de privilèges d'administrateur.", - "xpack.fleet.agentEnrollment.stepRunAgentTitle": "Démarrer l'agent", - "xpack.fleet.agentEnrollment.stepViewDataTitle": "Consulter vos données", - "xpack.fleet.agentEnrollment.viewDataDescription": "Une fois que votre agent a démarré, vous pouvez consulter vos données dans Kibana en utilisant les composants de l'intégration installés. {pleaseNote} : l'affichage des données initiales peut prendre quelques minutes.", "xpack.fleet.agentHealth.checkInTooltipText": "Dernier archivage le {lastCheckIn}", "xpack.fleet.agentHealth.healthyStatusText": "Sain", "xpack.fleet.agentHealth.inactiveStatusText": "Inactif", @@ -10133,8 +10125,6 @@ "xpack.fleet.enrollmentTokensList.secretTitle": "Secret", "xpack.fleet.enrollmentTokensList.showTokenButtonLabel": "Afficher le jeton", "xpack.fleet.epm.addPackagePolicyButtonText": "Ajouter {packageName}", - "xpack.fleet.epm.agentEnrollment.viewDataAssetsLabel": "Voir les ressources", - "xpack.fleet.epm.agentEnrollment.viewDataDescription.pleaseNoteLabel": "Remarque", "xpack.fleet.epm.assetGroupTitle": "Ressources {assetType}", "xpack.fleet.epm.browseAllButtonText": "Parcourir toutes les intégrations", "xpack.fleet.epm.categoryLabel": "Catégorie", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 45eff378b56f9..1527acb993bb4 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -11540,19 +11540,14 @@ "xpack.fleet.agentEnrollment.downloadPolicyButton": "ポリシーのダウンロード", "xpack.fleet.agentEnrollment.downloadPolicyButtonk8s": "マニフェストのダウンロード", "xpack.fleet.agentEnrollment.downloadUseLinuxInstaller": "Linuxユーザー:Fleetでエージェントをアップグレードできるため、システムパッケージ(RPM/DEB)ではインストーラー(TAR)をお勧めします。", - "xpack.fleet.agentEnrollment.enrollFleetTabLabel": "Fleetで登録", - "xpack.fleet.agentEnrollment.enrollStandaloneTabLabel": "スタンドアロンで実行", "xpack.fleet.agentEnrollment.fleetSettingsLink": "Fleet設定", "xpack.fleet.agentEnrollment.flyoutTitle": "エージェントの追加", - "xpack.fleet.agentEnrollment.goToDataStreamsLink": "データストリーム", "xpack.fleet.agentEnrollment.managedDescription": "ElasticエージェントをFleetに登録して、自動的に更新をデプロイしたり、一元的にエージェントを管理したりします。", "xpack.fleet.agentEnrollment.missingFleetHostCalloutText": "Fleetにエージェントを登録するには、FleetサーバーホストのURLが必要です。Fleet設定でこの情報を追加できます。詳細は{link}をご覧ください。", "xpack.fleet.agentEnrollment.missingFleetHostCalloutTitle": "FleetサーバーホストのURLが見つかりません", "xpack.fleet.agentEnrollment.missingFleetHostGuideLink": "FleetおよびElasticエージェントガイド", "xpack.fleet.agentEnrollment.setUpAgentsLink": "Elasticエージェントの集中管理を設定", "xpack.fleet.agentEnrollment.standaloneDescription": "Elasticエージェントをスタンドアロンで実行して、エージェントがインストールされているホストで、手動でエージェントを構成および更新します。", - "xpack.fleet.agentEnrollment.stepCheckForDataDescription": "エージェントがデータの送信を開始します。{link}に移動して、データを表示してください。", - "xpack.fleet.agentEnrollment.stepCheckForDataTitle": "データを確認", "xpack.fleet.agentEnrollment.stepChooseAgentPolicyTitle": "追加しているホストのタイプ", "xpack.fleet.agentEnrollment.stepConfigureAgentDescription": "Elasticエージェントがインストールされているホストで、このポリシーを{fileName}にコピーします。Elasticsearch資格情報を使用するには、{fileName}の{outputSection}セクションで、{ESUsernameVariable}と{ESPasswordVariable}を変更します。", "xpack.fleet.agentEnrollment.stepConfigureAgentDescriptionk8s": "Kubernetesクラスター内でKubernetesマニフェストをコピーしてダウンロードします。Daemonset環境変数で{ESUsernameVariable}と{ESPasswordVariable}を修正し、マニフェストを適用します。", @@ -11563,9 +11558,6 @@ "xpack.fleet.agentEnrollment.stepEnrollAndRunAgentTitle": "Elasticエージェントを登録して実行", "xpack.fleet.agentEnrollment.stepRunAgentDescription": "エージェントのディレクトリから、このコマンドを実行し、Elasticエージェントを、インストール、登録、起動します。このコマンドを再利用すると、複数のホストでエージェントを設定できます。管理者権限が必要です。", "xpack.fleet.agentEnrollment.stepRunAgentDescriptionk8s": "Kubernetesマニフェストがダウンロードされるディレクトリから適用コマンドを実行します。", - "xpack.fleet.agentEnrollment.stepRunAgentTitle": "エージェントの起動", - "xpack.fleet.agentEnrollment.stepViewDataTitle": "データを表示", - "xpack.fleet.agentEnrollment.viewDataDescription": "エージェントが起動した後、Kibanaでデータを表示するには、統合のインストールされたアセットを使用します。{pleaseNote}:初期データを受信するまでに数分かかる場合があります。", "xpack.fleet.agentHealth.checkInTooltipText": "前回のチェックイン {lastCheckIn}", "xpack.fleet.agentHealth.healthyStatusText": "正常", "xpack.fleet.agentHealth.inactiveStatusText": "非アクティブ", @@ -11864,8 +11856,6 @@ "xpack.fleet.epm.addPackagePolicyButtonPrivilegesRequiredTooltip": "Elasticエージェント統合には、Fleetの「すべて」権限と統合の「すべて」権限が必要です。管理者にお問い合わせください。", "xpack.fleet.epm.addPackagePolicyButtonSecurityRequiredTooltip": "Elasticエージェント統合を追加するには、セキュリティを有効にし、Fleetの「すべて」権限が必要です。管理者にお問い合わせください。", "xpack.fleet.epm.addPackagePolicyButtonText": "{packageName}の追加", - "xpack.fleet.epm.agentEnrollment.viewDataAssetsLabel": "アセットを表示", - "xpack.fleet.epm.agentEnrollment.viewDataDescription.pleaseNoteLabel": "注記:", "xpack.fleet.epm.assetGroupTitle": "{assetType}アセット", "xpack.fleet.epm.assetTitles.componentTemplates": "コンポーネントテンプレート", "xpack.fleet.epm.assetTitles.dashboards": "ダッシュボード", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index ff9dfba86005e..70d58abb23a95 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -11559,19 +11559,14 @@ "xpack.fleet.agentEnrollment.downloadPolicyButton": "下载策略", "xpack.fleet.agentEnrollment.downloadPolicyButtonk8s": "下载清单", "xpack.fleet.agentEnrollment.downloadUseLinuxInstaller": "Linux 用户:我们建议使用安装程序 (TAR),而非系统软件包 (RPM/DEB),因为这允许您在 Fleet 中升级代理。", - "xpack.fleet.agentEnrollment.enrollFleetTabLabel": "在 Fleet 中注册", - "xpack.fleet.agentEnrollment.enrollStandaloneTabLabel": "独立运行", "xpack.fleet.agentEnrollment.fleetSettingsLink": "Fleet 设置", "xpack.fleet.agentEnrollment.flyoutTitle": "添加代理", - "xpack.fleet.agentEnrollment.goToDataStreamsLink": "数据流", "xpack.fleet.agentEnrollment.managedDescription": "在 Fleet 中注册 Elastic 代理,以便自动部署更新并集中管理该代理。", "xpack.fleet.agentEnrollment.missingFleetHostCalloutText": "需要 Fleet 服务器主机的 URL,才能使用 Fleet 注册代理。可以在“Fleet 设置”中添加此信息。有关更多信息,请参阅{link}。", "xpack.fleet.agentEnrollment.missingFleetHostCalloutTitle": "Fleet 服务器主机的 URL 缺失", "xpack.fleet.agentEnrollment.missingFleetHostGuideLink": "Fleet 和 Elastic 代理指南", "xpack.fleet.agentEnrollment.setUpAgentsLink": "为 Elastic 代理设置集中管理", "xpack.fleet.agentEnrollment.standaloneDescription": "独立运行 Elastic 代理,以在安装代理的主机上手动配置和更新代理。", - "xpack.fleet.agentEnrollment.stepCheckForDataDescription": "该代理应该开始发送数据。前往 {link} 以查看您的数据。", - "xpack.fleet.agentEnrollment.stepCheckForDataTitle": "检查数据", "xpack.fleet.agentEnrollment.stepChooseAgentPolicyTitle": "您正在添加什么类型的主机?", "xpack.fleet.agentEnrollment.stepConfigureAgentDescription": "在安装 Elastic 代理的主机上将此策略复制到 {fileName}。在 {fileName} 的 {outputSection} 部分中修改 {ESUsernameVariable} 和 {ESPasswordVariable},以使用您的 Elasticsearch 凭据。", "xpack.fleet.agentEnrollment.stepConfigureAgentDescriptionk8s": "复制或下载 Kubernetes 集群内的 Kubernetes 清单。修改 Daemonset 环境变量中的 {ESUsernameVariable} 和 {ESPasswordVariable} 并应用该清单。", @@ -11582,9 +11577,6 @@ "xpack.fleet.agentEnrollment.stepEnrollAndRunAgentTitle": "注册并启动 Elastic 代理", "xpack.fleet.agentEnrollment.stepRunAgentDescription": "从代理目录运行此命令,以安装、注册并启动 Elastic 代理。您可以重复使用此命令在多个主机上设置代理。需要管理员权限。", "xpack.fleet.agentEnrollment.stepRunAgentDescriptionk8s": "从下载 Kubernetes 清单的目录运行应用命令。", - "xpack.fleet.agentEnrollment.stepRunAgentTitle": "启动代理", - "xpack.fleet.agentEnrollment.stepViewDataTitle": "查看您的数据", - "xpack.fleet.agentEnrollment.viewDataDescription": "代理启动后,可以通过使用集成的已安装资产来在 Kibana 中查看数据。{pleaseNote}:获得初始数据可能需要几分钟。", "xpack.fleet.agentHealth.checkInTooltipText": "上次签入时间 {lastCheckIn}", "xpack.fleet.agentHealth.healthyStatusText": "运行正常", "xpack.fleet.agentHealth.inactiveStatusText": "非活动", @@ -11885,8 +11877,6 @@ "xpack.fleet.epm.addPackagePolicyButtonPrivilegesRequiredTooltip": "Elastic 代理集成需要 Fleet 的所有权限和集成的所有权限。请联系您的管理员。", "xpack.fleet.epm.addPackagePolicyButtonSecurityRequiredTooltip": "要添加 Elastic 代理集成,必须启用安全功能并具有 Fleet 的所有权限。请联系您的管理员。", "xpack.fleet.epm.addPackagePolicyButtonText": "添加 {packageName}", - "xpack.fleet.epm.agentEnrollment.viewDataAssetsLabel": "查看资产", - "xpack.fleet.epm.agentEnrollment.viewDataDescription.pleaseNoteLabel": "请注意", "xpack.fleet.epm.assetGroupTitle": "{assetType} 资产", "xpack.fleet.epm.assetTitles.componentTemplates": "组件模板", "xpack.fleet.epm.assetTitles.dashboards": "仪表板", From dcbf9daa0029134c71fe14488b630c980a718159 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 11 Apr 2022 11:38:23 -0500 Subject: [PATCH 40/42] skip failing test suite (#129785) --- test/functional/apps/visualize/_tsvb_time_series.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/visualize/_tsvb_time_series.ts b/test/functional/apps/visualize/_tsvb_time_series.ts index b474f2e68cb90..6180797cb886b 100644 --- a/test/functional/apps/visualize/_tsvb_time_series.ts +++ b/test/functional/apps/visualize/_tsvb_time_series.ts @@ -26,7 +26,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const browser = getService('browser'); const kibanaServer = getService('kibanaServer'); - describe('visual builder', function describeIndexTests() { + // Failing: See https://github.com/elastic/kibana/issues/129785 + describe.skip('visual builder', function describeIndexTests() { before(async () => { await security.testUser.setRoles([ 'kibana_admin', From 47b62d83af4823f94b03fae4190e56df8726baae Mon Sep 17 00:00:00 2001 From: Robert Austin Date: Mon, 11 Apr 2022 12:42:33 -0400 Subject: [PATCH 41/42] Enhance Event Fields Browser performance (#129861) * Enhance Event Fields Browser performance * fixes checks * Update x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx Use idiomatic value for EUI's `itemId` field Co-authored-by: Jan Monschke Co-authored-by: Gloria Hornero Co-authored-by: Jan Monschke --- .../event_details/event_fields_browser.tsx | 1 + x-pack/plugins/timelines/public/plugin.ts | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx index 29700d3706b7b..e7547a8259514 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx @@ -279,6 +279,7 @@ export const EventFieldsBrowser = React.memo( { private _store: Store | undefined; private _storage = new Storage(localStorage); private _storeUnsubscribe: Unsubscribe | undefined; + private _hoverActions: HoverActionsConfig | undefined; + public setup(core: CoreSetup) {} public start(core: CoreStart, { data }: TimelinesStartPlugins): TimelinesUIStart { return { + /** `getHoverActions` returns a new reference to `getAddToTimelineButton` each time it is called, but that value is used in dependency arrays and so it should be as stable as possible. Therefore we lazily store the reference to it. Note: this reference is deleted when the store is changed. */ getHoverActions: () => { - return getHoverActions(this._store); + if (this._hoverActions) { + return this._hoverActions; + } else { + this._hoverActions = getHoverActions(this._store); + return this._hoverActions; + } }, getTGrid: (props: TGridProps) => { if (props.type === 'standalone' && this._store) { @@ -89,6 +97,8 @@ export class TimelinesPlugin implements Plugin { private setStore(store: Store) { this._store = store; + // this is lazily calculated and that is dependent on the store + delete this._hoverActions; } public stop() { From 9b7cde9e6caf47b8b6a139c74a22713b14e2db41 Mon Sep 17 00:00:00 2001 From: gchaps <33642766+gchaps@users.noreply.github.com> Date: Mon, 11 Apr 2022 09:43:06 -0700 Subject: [PATCH 42/42] [DOCS] Fixes formatting of settings (#129400) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- docs/setup/settings.asciidoc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index c0b37ba9ec6f3..c8fa521f93947 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -689,29 +689,29 @@ sources and images. When false, Vega can only get data from {es}. *Default: `fal | Enables you to view the underlying documents in a data series from a dashboard panel. *Default: `false`* | `xpack.ilm.ui.enabled` -Set this value to false to disable the Index Lifecycle Policies UI. + | Set this value to false to disable the Index Lifecycle Policies UI. *Default: `true`* | `xpack.index_management.ui.enabled` -Set this value to false to disable the Index Management UI. + | Set this value to false to disable the Index Management UI. *Default: `true`* | `xpack.license_management.ui.enabled` -Set this value to false to disable the License Management UI. + | Set this value to false to disable the License Management UI. *Default: `true`* | `xpack.remote_clusters.ui.enabled` -Set this value to false to disable the Remote Clusters UI. + | Set this value to false to disable the Remote Clusters UI. *Default: `true`* | `xpack.rollup.ui.enabled:` -Set this value to false to disable the Rollup Jobs UI. *Default: true* + | Set this value to false to disable the Rollup Jobs UI. *Default: true* | `xpack.snapshot_restore.ui.enabled:` -Set this value to false to disable the Snapshot and Restore UI. *Default: true* + | Set this value to false to disable the Snapshot and Restore UI. *Default: true* | `xpack.upgrade_assistant.ui.enabled:` -Set this value to false to disable the Upgrade Assistant UI. *Default: true* + | Set this value to false to disable the Upgrade Assistant UI. *Default: true* | `i18n.locale` {ess-icon} | Set this value to change the {kib} interface language.