From 597e69c9db6a9acc13eae0dc06d5bee5ee33e3a2 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Thu, 16 Sep 2021 11:44:13 +0200 Subject: [PATCH 1/8] [Discover] Set ownfocus to false when displaying the document flyout (#108646) (#112379) # Conflicts: # test/functional/apps/discover/_data_grid_doc_table.ts --- .../application/components/discover_grid/constants.ts | 1 + .../discover_grid/discover_grid_columns.test.tsx | 2 +- .../components/discover_grid/discover_grid_columns.tsx | 5 +++-- .../components/discover_grid/discover_grid_flyout.tsx | 3 ++- test/functional/apps/discover/_data_grid_doc_table.ts | 10 ++++++++++ 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/plugins/discover/public/application/components/discover_grid/constants.ts b/src/plugins/discover/public/application/components/discover_grid/constants.ts index 34e6ca20740ad..1126c6cbbf279 100644 --- a/src/plugins/discover/public/application/components/discover_grid/constants.ts +++ b/src/plugins/discover/public/application/components/discover_grid/constants.ts @@ -17,6 +17,7 @@ export const gridStyle = { export const pageSizeArr = [25, 50, 100, 250]; export const defaultPageSize = 100; +export const defaultTimeColumnWidth = 190; export const toolbarVisibility = { showColumnSelector: { allowHide: false, diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx index 3cbac90aa39cb..46e30dd23525b 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx @@ -119,7 +119,7 @@ describe('Discover grid columns ', function () { ], "display": "Time (timestamp)", "id": "timestamp", - "initialWidth": 180, + "initialWidth": 190, "isSortable": true, "schema": "datetime", }, diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx index 3a27772662b56..2f4c0b5167df8 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx @@ -11,10 +11,11 @@ import { i18n } from '@kbn/i18n'; import { EuiDataGridColumn, EuiScreenReaderOnly } from '@elastic/eui'; import { ExpandButton } from './discover_grid_expand_button'; import { DiscoverGridSettings } from './types'; -import { IndexPattern } from '../../../../../data/common/index_patterns/index_patterns'; +import type { IndexPattern } from '../../../../../data/common'; import { buildCellActions } from './discover_grid_cell_actions'; import { getSchemaByKbnType } from './discover_grid_schema'; import { SelectButton } from './discover_grid_document_selection'; +import { defaultTimeColumnWidth } from './constants'; export function getLeadControlColumns() { return [ @@ -88,7 +89,7 @@ export function buildEuiGridColumn( if (column.id === indexPattern.timeFieldName) { column.display = `${timeString} (${indexPattern.timeFieldName})`; - column.initialWidth = 180; + column.initialWidth = defaultTimeColumnWidth; } if (columnWidth > 0) { column.initialWidth = Number(columnWidth); diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx index c5b75dbe85aea..e4b67c49689ab 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx @@ -107,6 +107,7 @@ export function DiscoverGridFlyout({ size="m" data-test-subj="docTableDetailsFlyout" onKeyDown={onKeyDown} + ownFocus={false} > )} {activePage !== -1 && ( - + Date: Thu, 16 Sep 2021 13:04:33 +0200 Subject: [PATCH 2/8] [Upgrade Assistant] Add integration tests for Overview page (#111370) * Add a11y tests for when overview page has toggle enabled * Add functional and accessibility tests for overview page * Load test files * Fix linter error * Navigate before asserting * Steps have now completion state * Remove duped word * Run setup only once, not per test * Address CR changes * No need to renavigate to the page Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../overview/upgrade_step/upgrade_step.tsx | 1 + .../accessibility/apps/upgrade_assistant.ts | 24 +++++-- ...rade_assistant.ts => deprecation_pages.ts} | 9 +-- .../apps/upgrade_assistant/index.ts | 3 +- .../apps/upgrade_assistant/overview_page.ts | 68 +++++++++++++++++++ .../page_objects/upgrade_assistant_page.ts | 12 ++++ 6 files changed, 104 insertions(+), 13 deletions(-) rename x-pack/test/functional/apps/upgrade_assistant/{upgrade_assistant.ts => deprecation_pages.ts} (90%) create mode 100644 x-pack/test/functional/apps/upgrade_assistant/overview_page.ts diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/upgrade_step/upgrade_step.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/upgrade_step/upgrade_step.tsx index cb8c88b9d2af2..dffdf39e5dd59 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/upgrade_step/upgrade_step.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/upgrade_step/upgrade_step.tsx @@ -123,6 +123,7 @@ export const getUpgradeStep = ({ docLinks, nextMajor }: Props): EuiStepProps => return { title: i18nTexts.upgradeStepTitle(nextMajor), status: 'incomplete', + 'data-test-subj': 'upgradeStep', children: , }; }; diff --git a/x-pack/test/accessibility/apps/upgrade_assistant.ts b/x-pack/test/accessibility/apps/upgrade_assistant.ts index 690f258198ebc..452f6051e0640 100644 --- a/x-pack/test/accessibility/apps/upgrade_assistant.ts +++ b/x-pack/test/accessibility/apps/upgrade_assistant.ts @@ -77,11 +77,27 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { } }); - it('Overview page', async () => { - await retry.waitFor('Upgrade Assistant overview page to be visible', async () => { - return testSubjects.exists('overview'); + describe('Overview page', () => { + beforeEach(async () => { + await PageObjects.upgradeAssistant.navigateToPage(); + await retry.waitFor('Upgrade Assistant overview page to be visible', async () => { + return testSubjects.exists('overview'); + }); + }); + + it('with logs collection disabled', async () => { + await a11y.testAppSnapshot(); + }); + + it('with logs collection enabled', async () => { + await PageObjects.upgradeAssistant.clickDeprecationLoggingToggle(); + + await retry.waitFor('UA external links title to be present', async () => { + return testSubjects.isDisplayed('externalLinksTitle'); + }); + + await a11y.testAppSnapshot(); }); - await a11y.testAppSnapshot(); }); describe('Elasticsearch deprecations page', () => { diff --git a/x-pack/test/functional/apps/upgrade_assistant/upgrade_assistant.ts b/x-pack/test/functional/apps/upgrade_assistant/deprecation_pages.ts similarity index 90% rename from x-pack/test/functional/apps/upgrade_assistant/upgrade_assistant.ts rename to x-pack/test/functional/apps/upgrade_assistant/deprecation_pages.ts index 93af80d5e9501..322cae2e5fd56 100644 --- a/x-pack/test/functional/apps/upgrade_assistant/upgrade_assistant.ts +++ b/x-pack/test/functional/apps/upgrade_assistant/deprecation_pages.ts @@ -55,7 +55,7 @@ export default function upgradeAssistantFunctionalTests({ const es = getService('es'); const log = getService('log'); - describe('Upgrade Assistant', function () { + describe('Deprecation pages', function () { this.tags('skipFirefox'); before(async () => { @@ -84,13 +84,6 @@ export default function upgradeAssistantFunctionalTests({ await security.testUser.restoreDefaults(); }); - it('renders the Overview page', async () => { - await PageObjects.upgradeAssistant.navigateToPage(); - await retry.waitFor('Upgrade Assistant overview page to be visible', async () => { - return testSubjects.exists('overview'); - }); - }); - it('renders the Elasticsearch deprecations page', async () => { await PageObjects.upgradeAssistant.navigateToPage(); await PageObjects.upgradeAssistant.clickEsDeprecationsPanel(); diff --git a/x-pack/test/functional/apps/upgrade_assistant/index.ts b/x-pack/test/functional/apps/upgrade_assistant/index.ts index cacf55c58dce2..d99d1cd033327 100644 --- a/x-pack/test/functional/apps/upgrade_assistant/index.ts +++ b/x-pack/test/functional/apps/upgrade_assistant/index.ts @@ -12,6 +12,7 @@ export default function upgradeCheckup({ loadTestFile }: FtrProviderContext) { this.tags('ciGroup4'); loadTestFile(require.resolve('./feature_controls')); - loadTestFile(require.resolve('./upgrade_assistant')); + loadTestFile(require.resolve('./deprecation_pages')); + loadTestFile(require.resolve('./overview_page')); }); } diff --git a/x-pack/test/functional/apps/upgrade_assistant/overview_page.ts b/x-pack/test/functional/apps/upgrade_assistant/overview_page.ts new file mode 100644 index 0000000000000..76663a9f57c89 --- /dev/null +++ b/x-pack/test/functional/apps/upgrade_assistant/overview_page.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function upgradeAssistantOverviewPageFunctionalTests({ + getService, + getPageObjects, +}: FtrProviderContext) { + const PageObjects = getPageObjects(['upgradeAssistant', 'common']); + const retry = getService('retry'); + const security = getService('security'); + const testSubjects = getService('testSubjects'); + const es = getService('es'); + + describe('Overview Page', function () { + this.tags('skipFirefox'); + + before(async () => { + await security.testUser.setRoles(['superuser']); + }); + + after(async () => { + await security.testUser.restoreDefaults(); + }); + + beforeEach(async () => { + await PageObjects.upgradeAssistant.navigateToPage(); + + await retry.waitFor('Upgrade Assistant overview page to be visible', async () => { + return testSubjects.exists('overview'); + }); + }); + + it('Should render all steps', async () => { + testSubjects.exists('backupStep-incomplete'); + testSubjects.exists('fixIssuesStep-incomplete'); + testSubjects.exists('fixLogsStep-incomplete'); + testSubjects.exists('upgradeStep'); + }); + + describe('fixLogsStep', () => { + before(async () => { + // Access to system indices will be deprecated and should generate a deprecation log + await es.indices.get({ index: '.kibana' }); + + await PageObjects.upgradeAssistant.clickDeprecationLoggingToggle(); + + await retry.waitFor('UA external links title to be present', async () => { + return testSubjects.isDisplayed('externalLinksTitle'); + }); + }); + + it('Shows warnings callout if there are deprecations', async () => { + testSubjects.exists('hasWarningsCallout'); + }); + + it('Shows no warnings callout if there are no deprecations', async () => { + await PageObjects.upgradeAssistant.clickResetLastCheckpointButton(); + testSubjects.exists('noWarningsCallout'); + }); + }); + }); +} diff --git a/x-pack/test/functional/page_objects/upgrade_assistant_page.ts b/x-pack/test/functional/page_objects/upgrade_assistant_page.ts index e763145a715ff..54d7f3d452123 100644 --- a/x-pack/test/functional/page_objects/upgrade_assistant_page.ts +++ b/x-pack/test/functional/page_objects/upgrade_assistant_page.ts @@ -35,6 +35,18 @@ export class UpgradeAssistantPageObject extends FtrService { }); } + async clickDeprecationLoggingToggle() { + return await this.retry.try(async () => { + await this.testSubjects.click('deprecationLoggingToggle'); + }); + } + + async clickResetLastCheckpointButton() { + return await this.retry.try(async () => { + await this.testSubjects.click('resetLastStoredDate'); + }); + } + async clickKibanaDeprecationsPanel() { return await this.retry.try(async () => { await this.testSubjects.click('kibanaStatsPanel'); From 198ba3428079cfa23526d399520376182e023223 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 16 Sep 2021 07:57:58 -0400 Subject: [PATCH 3/8] fix data loss mssage (#112294) (#112389) Co-authored-by: Joe Reuter --- .../editor_frame/workspace_panel/chart_switch.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx index 010e4d73c4791..28c0567d784ea 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx @@ -382,7 +382,7 @@ export const ChartSwitch = memo(function ChartSwitch(props: Props) { 'xpack.lens.chartSwitch.dataLossDescription', { defaultMessage: - 'Selecting this visualization type will result in a partial loss of currently applied configuration selections.', + 'Selecting this visualization type will remove incompatible configuration options and multiple layers, if present', } )} iconProps={{ From 0e76f30e90982794ff9abc9f4ac150bb24f8d837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Thu, 16 Sep 2021 08:09:56 -0400 Subject: [PATCH 4/8] Removing old e2e (#112170) (#112358) * removing old e2e * removing e2e * removing e2e Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> # Conflicts: # .github/CODEOWNERS # .github/paths-labeller.yml --- .eslintignore | 1 - .../src/get_server_watch_paths.test.ts | 1 - .../src/get_server_watch_paths.ts | 1 - src/dev/precommit_hook/casing_check_config.js | 1 - src/dev/typescript/projects.ts | 4 - x-pack/plugins/apm/e2e/.gitignore | 5 - x-pack/plugins/apm/e2e/README.md | 15 -- x-pack/plugins/apm/e2e/ci/kibana.e2e.yml | 31 --- x-pack/plugins/apm/e2e/ci/prepare-kibana.sh | 13 -- x-pack/plugins/apm/e2e/ci/run-e2e.sh | 11 -- x-pack/plugins/apm/e2e/cypress.json | 27 --- .../apm/e2e/cypress/fixtures/example.json | 5 - .../cypress/integration/csm_dashboard.feature | 34 ---- .../apm/e2e/cypress/integration/helpers.ts | 36 ---- .../apm/e2e/cypress/integration/snapshots.js | 3 - .../apm/e2e/cypress/integration/snapshots.ts | 10 - .../plugins/apm/e2e/cypress/plugins/index.js | 43 ----- .../apm/e2e/cypress/support/commands.js | 32 ---- .../plugins/apm/e2e/cypress/support/index.ts | 28 --- .../step_definitions/csm/breakdown_filter.ts | 43 ----- .../csm/client_metrics_helper.ts | 36 ---- .../step_definitions/csm/csm_dashboard.ts | 77 -------- .../step_definitions/csm/csm_filters.ts | 65 ------- .../support/step_definitions/csm/js_errors.ts | 24 --- .../step_definitions/csm/percentile_select.ts | 22 --- .../csm/service_name_filter.ts | 22 --- .../step_definitions/csm/url_search_filter.ts | 66 ------- .../support/step_definitions/csm/utils.ts | 18 -- .../apm/e2e/cypress/typings/index.d.ts | 12 -- .../plugins/apm/e2e/cypress/webpack.config.js | 44 ----- x-pack/plugins/apm/e2e/ingest-data/replay.js | 173 ----------------- x-pack/plugins/apm/e2e/ingest-data/rum_ips.js | 20 -- .../apm/e2e/ingest-data/user_agents.js | 24 --- x-pack/plugins/apm/e2e/run-e2e.sh | 176 ------------------ x-pack/plugins/apm/e2e/tsconfig.json | 9 - x-pack/plugins/apm/jest.config.js | 1 - .../scripts/optimize-tsconfig/tsconfig.json | 1 - 37 files changed, 1134 deletions(-) delete mode 100644 x-pack/plugins/apm/e2e/.gitignore delete mode 100644 x-pack/plugins/apm/e2e/README.md delete mode 100644 x-pack/plugins/apm/e2e/ci/kibana.e2e.yml delete mode 100755 x-pack/plugins/apm/e2e/ci/prepare-kibana.sh delete mode 100755 x-pack/plugins/apm/e2e/ci/run-e2e.sh delete mode 100644 x-pack/plugins/apm/e2e/cypress.json delete mode 100644 x-pack/plugins/apm/e2e/cypress/fixtures/example.json delete mode 100644 x-pack/plugins/apm/e2e/cypress/integration/csm_dashboard.feature delete mode 100644 x-pack/plugins/apm/e2e/cypress/integration/helpers.ts delete mode 100644 x-pack/plugins/apm/e2e/cypress/integration/snapshots.js delete mode 100644 x-pack/plugins/apm/e2e/cypress/integration/snapshots.ts delete mode 100644 x-pack/plugins/apm/e2e/cypress/plugins/index.js delete mode 100644 x-pack/plugins/apm/e2e/cypress/support/commands.js delete mode 100644 x-pack/plugins/apm/e2e/cypress/support/index.ts delete mode 100644 x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/breakdown_filter.ts delete mode 100644 x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/client_metrics_helper.ts delete mode 100644 x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/csm_dashboard.ts delete mode 100644 x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/csm_filters.ts delete mode 100644 x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/js_errors.ts delete mode 100644 x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/percentile_select.ts delete mode 100644 x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/service_name_filter.ts delete mode 100644 x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/url_search_filter.ts delete mode 100644 x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/utils.ts delete mode 100644 x-pack/plugins/apm/e2e/cypress/typings/index.d.ts delete mode 100644 x-pack/plugins/apm/e2e/cypress/webpack.config.js delete mode 100644 x-pack/plugins/apm/e2e/ingest-data/replay.js delete mode 100644 x-pack/plugins/apm/e2e/ingest-data/rum_ips.js delete mode 100644 x-pack/plugins/apm/e2e/ingest-data/user_agents.js delete mode 100755 x-pack/plugins/apm/e2e/run-e2e.sh delete mode 100644 x-pack/plugins/apm/e2e/tsconfig.json diff --git a/.eslintignore b/.eslintignore index c848fc0797907..62287e1f06911 100644 --- a/.eslintignore +++ b/.eslintignore @@ -22,7 +22,6 @@ snapshots.js /src/core/lib/kbn_internal_native_observable /src/plugins/data/common/es_query/kuery/ast/_generated_/** /src/plugins/vis_types/timelion/common/_generated_/** -/x-pack/plugins/apm/e2e/tmp/* /x-pack/plugins/canvas/canvas_plugin /x-pack/plugins/canvas/shareable_runtime/build /x-pack/plugins/canvas/storybook/build diff --git a/packages/kbn-cli-dev-mode/src/get_server_watch_paths.test.ts b/packages/kbn-cli-dev-mode/src/get_server_watch_paths.test.ts index f4017df600a48..1a49faabd8da3 100644 --- a/packages/kbn-cli-dev-mode/src/get_server_watch_paths.test.ts +++ b/packages/kbn-cli-dev-mode/src/get_server_watch_paths.test.ts @@ -67,7 +67,6 @@ it('produces the right watch and ignore list', () => { /x-pack/test/plugin_functional/plugins/resolver_test/docs/**, /x-pack/plugins/reporting/chromium, /x-pack/plugins/security_solution/cypress, - /x-pack/plugins/apm/e2e, /x-pack/plugins/apm/scripts, /x-pack/plugins/canvas/canvas_plugin_src, /x-pack/plugins/cases/server/scripts, diff --git a/packages/kbn-cli-dev-mode/src/get_server_watch_paths.ts b/packages/kbn-cli-dev-mode/src/get_server_watch_paths.ts index b0773fd567635..f08e456940808 100644 --- a/packages/kbn-cli-dev-mode/src/get_server_watch_paths.ts +++ b/packages/kbn-cli-dev-mode/src/get_server_watch_paths.ts @@ -58,7 +58,6 @@ export function getServerWatchPaths({ pluginPaths, pluginScanDirs }: Options) { ...pluginInternalDirsIgnore, fromRoot('x-pack/plugins/reporting/chromium'), fromRoot('x-pack/plugins/security_solution/cypress'), - fromRoot('x-pack/plugins/apm/e2e'), fromRoot('x-pack/plugins/apm/scripts'), fromRoot('x-pack/plugins/canvas/canvas_plugin_src'), // prevents server from restarting twice for Canvas plugin changes, fromRoot('x-pack/plugins/cases/server/scripts'), diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index 401ff60ee4187..b37f3308522f0 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -59,7 +59,6 @@ export const IGNORE_FILE_GLOBS = [ // TODO fix file names in APM to remove these 'x-pack/plugins/apm/public/**/*', 'x-pack/plugins/apm/scripts/**/*', - 'x-pack/plugins/apm/e2e/**/*', 'x-pack/plugins/maps/server/fonts/**/*', diff --git a/src/dev/typescript/projects.ts b/src/dev/typescript/projects.ts index e3d8185e73e55..a9e1e0da8e97f 100644 --- a/src/dev/typescript/projects.ts +++ b/src/dev/typescript/projects.ts @@ -59,10 +59,6 @@ export const PROJECTS = [ createProject('x-pack/plugins/osquery/cypress/tsconfig.json', { name: 'osquery/cypress', }), - createProject('x-pack/plugins/apm/e2e/tsconfig.json', { - name: 'apm/cypress', - disableTypeCheck: true, - }), createProject('x-pack/plugins/apm/ftr_e2e/tsconfig.json', { name: 'apm/ftr_e2e', disableTypeCheck: true, diff --git a/x-pack/plugins/apm/e2e/.gitignore b/x-pack/plugins/apm/e2e/.gitignore deleted file mode 100644 index 5042f0bca0300..0000000000000 --- a/x-pack/plugins/apm/e2e/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -cypress/screenshots/* -cypress/test-results -cypress/videos/* -/snapshots.js -tmp diff --git a/x-pack/plugins/apm/e2e/README.md b/x-pack/plugins/apm/e2e/README.md deleted file mode 100644 index 3517c74e950c7..0000000000000 --- a/x-pack/plugins/apm/e2e/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# End-To-End (e2e) Test for APM UI - -**Run E2E tests** - - -```sh -# In one terminal -node ./scripts/kibana --no-base-path --dev --no-dev-config --config x-pack/plugins/apm/e2e/ci/kibana.e2e.yml -# In another terminal -x-pack/plugins/apm/e2e/run-e2e.sh -``` - -Starts kibana, APM Server, Elasticsearch (with sample data) and runs the tests. - -If you see errors about not all events being ingested correctly try running `cd kibana/x-pack/plugins/apm/e2e/tmp/apm-integration-testing && docker-compose down -v` diff --git a/x-pack/plugins/apm/e2e/ci/kibana.e2e.yml b/x-pack/plugins/apm/e2e/ci/kibana.e2e.yml deleted file mode 100644 index 19f3f7c8978fa..0000000000000 --- a/x-pack/plugins/apm/e2e/ci/kibana.e2e.yml +++ /dev/null @@ -1,31 +0,0 @@ -# Kibana -server.port: 5701 -xpack.security.encryptionKey: 'something_at_least_32_characters' -csp.strict: false -logging.verbose: true - -# Elasticsearch -# Started via apm-integration-testing -# ./scripts/compose.py start master --no-kibana --elasticsearch-port 9201 --apm-server-port 8201 -elasticsearch.hosts: http://localhost:9201 -elasticsearch.username: 'kibana_system_user' -elasticsearch.password: 'changeme' - -# APM index pattern -apm_oss.indexPattern: apm-* - -# APM Indices -apm_oss.errorIndices: apm-*-error* -apm_oss.sourcemapIndices: apm-*-sourcemap -apm_oss.transactionIndices: apm-*-transaction* -apm_oss.spanIndices: apm-*-span* -apm_oss.metricsIndices: apm-*-metric* -apm_oss.onboardingIndices: apm-*-onboarding* - -# APM options -xpack.apm.enabled: true -xpack.apm.serviceMapEnabled: false -xpack.apm.autocreateApmIndexPattern: true -xpack.apm.ui.enabled: true -xpack.apm.ui.transactionGroupBucketSize: 100 -xpack.apm.ui.maxTraceItems: 1000 diff --git a/x-pack/plugins/apm/e2e/ci/prepare-kibana.sh b/x-pack/plugins/apm/e2e/ci/prepare-kibana.sh deleted file mode 100755 index 9e6198bcc526d..0000000000000 --- a/x-pack/plugins/apm/e2e/ci/prepare-kibana.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -set -e - -E2E_DIR=x-pack/plugins/apm/e2e -echo "1/2 Install dependencies..." -# shellcheck disable=SC1091 -source src/dev/ci_setup/setup_env.sh true -yarn kbn bootstrap - -echo "2/2 Start Kibana..." -## Might help to avoid FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory -export NODE_OPTIONS="--max-old-space-size=4096" -nohup node ./scripts/kibana --no-base-path --no-watch --dev --no-dev-config --config ${E2E_DIR}/ci/kibana.e2e.yml > ${E2E_DIR}/kibana.log 2>&1 & diff --git a/x-pack/plugins/apm/e2e/ci/run-e2e.sh b/x-pack/plugins/apm/e2e/ci/run-e2e.sh deleted file mode 100755 index c40ab5d646477..0000000000000 --- a/x-pack/plugins/apm/e2e/ci/run-e2e.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -## -## This is a wrapper to configure the environment with the right tools in the CI -## and run the e2e steps. -## - -E2E_DIR="${0%/*}/.." -# shellcheck disable=SC1091 -source src/dev/ci_setup/setup_env.sh true -set -ex -"${E2E_DIR}"/run-e2e.sh diff --git a/x-pack/plugins/apm/e2e/cypress.json b/x-pack/plugins/apm/e2e/cypress.json deleted file mode 100644 index 19e09e6ad5bbf..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "nodeVersion": "system", - "baseUrl": "http://localhost:5701", - "trashAssetsBeforeRuns": false, - "fileServerFolder": "../", - "fixturesFolder": "./cypress/fixtures", - "integrationFolder": "./cypress/integration", - "pluginsFile": "./cypress/plugins/index.js", - "screenshotsFolder": "./cypress/screenshots", - "supportFile": "./cypress/support/index.ts", - "video": true, - "videoCompression": false, - "videosFolder": "./cypress/videos", - "useRelativeSnapshots": true, - "reporter": "junit", - "reporterOptions": { - "mochaFile": "./cypress/test-results/[hash]-e2e-tests.xml", - "toConsole": false - }, - "testFiles": "**/*.{feature,features}", - "env": { - "elasticsearch_username": "admin", - "elasticsearch_password": "changeme" - }, - "viewportWidth": 1920, - "viewportHeight": 1080 -} diff --git a/x-pack/plugins/apm/e2e/cypress/fixtures/example.json b/x-pack/plugins/apm/e2e/cypress/fixtures/example.json deleted file mode 100644 index da18d9352a17d..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes" -} \ No newline at end of file diff --git a/x-pack/plugins/apm/e2e/cypress/integration/csm_dashboard.feature b/x-pack/plugins/apm/e2e/cypress/integration/csm_dashboard.feature deleted file mode 100644 index 0028f40a68d90..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/integration/csm_dashboard.feature +++ /dev/null @@ -1,34 +0,0 @@ -Feature: CSM Dashboard - - Scenario: Client metrics - When a user browses the APM UI application for RUM Data - Then should have correct client metrics - - Scenario: JS Errors - When a user browses the APM UI application for RUM Data - Then it displays list of relevant js errors - - Scenario: Percentile select - When the user changes the selected percentile - Then it displays client metric related to that percentile - - Scenario Outline: CSM page filters - When the user filters by "" - Then it filters the client metrics "" - Examples: - | filterName | - | OS | - | Location | - - Scenario: Display CSM Data components - When a user browses the APM UI application for RUM Data - Then should display percentile for page load chart - And should display tooltip on hover - - Scenario: Search by url filter focus - When a user clicks inside url search field - Then it displays top pages in the suggestion popover - - Scenario: Search by url filter - When a user enters a query in url search field - Then it should filter results based on query diff --git a/x-pack/plugins/apm/e2e/cypress/integration/helpers.ts b/x-pack/plugins/apm/e2e/cypress/integration/helpers.ts deleted file mode 100644 index 333f047b72658..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/integration/helpers.ts +++ /dev/null @@ -1,36 +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. - */ - -const BASE_URL = Cypress.config().baseUrl; - -/** The default time in ms to wait for a Cypress command to complete */ -export const DEFAULT_TIMEOUT = 60 * 1000; - -export function loginAndWaitForPage( - url: string, - dateRange: { to: string; from: string }, - selectedService?: string -) { - const username = Cypress.env('elasticsearch_username'); - const password = Cypress.env('elasticsearch_password'); - - cy.log(`Authenticating via ${username} / ${password}`); - - let fullUrl = `${BASE_URL}${url}?rangeFrom=${dateRange.from}&rangeTo=${dateRange.to}`; - - if (selectedService) { - fullUrl += `&serviceName=${selectedService}`; - } - cy.visit(fullUrl, { auth: { username, password } }); - - cy.viewport('macbook-15'); - - // wait for loading spinner to disappear - cy.get('#kbn_loading_message', { timeout: DEFAULT_TIMEOUT }).should( - 'not.exist' - ); -} diff --git a/x-pack/plugins/apm/e2e/cypress/integration/snapshots.js b/x-pack/plugins/apm/e2e/cypress/integration/snapshots.js deleted file mode 100644 index fc390a64de28c..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/integration/snapshots.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - "__version": "6.8.0" -} diff --git a/x-pack/plugins/apm/e2e/cypress/integration/snapshots.ts b/x-pack/plugins/apm/e2e/cypress/integration/snapshots.ts deleted file mode 100644 index 64946c113e777..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/integration/snapshots.ts +++ /dev/null @@ -1,10 +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. - */ - -module.exports = { - __version: '4.9.0', -}; diff --git a/x-pack/plugins/apm/e2e/cypress/plugins/index.js b/x-pack/plugins/apm/e2e/cypress/plugins/index.js deleted file mode 100644 index bec342118ed5b..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/plugins/index.js +++ /dev/null @@ -1,43 +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. - */ - -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -// eslint-disable-next-line import/no-extraneous-dependencies -const wp = require('@cypress/webpack-preprocessor'); -const fs = require('fs'); - -module.exports = (on) => { - const options = { - webpackOptions: require('../webpack.config.js'), - }; - on('file:preprocessor', wp(options)); - - // readFileMaybe - on('task', { - // ESLint thinks this is a react component for some reason. - // eslint-disable-next-line react/function-component-definition - readFileMaybe(filename) { - if (fs.existsSync(filename)) { - return fs.readFileSync(filename, 'utf8'); - } - - return null; - }, - }); -}; diff --git a/x-pack/plugins/apm/e2e/cypress/support/commands.js b/x-pack/plugins/apm/e2e/cypress/support/commands.js deleted file mode 100644 index 73895fbbec589..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/support/commands.js +++ /dev/null @@ -1,32 +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. - */ - -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add("login", (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This is will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/x-pack/plugins/apm/e2e/cypress/support/index.ts b/x-pack/plugins/apm/e2e/cypress/support/index.ts deleted file mode 100644 index 020156fb27915..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/support/index.ts +++ /dev/null @@ -1,28 +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. - */ - -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -import './commands'; - -// @ts-expect-error -import { register } from '@cypress/snapshot'; - -register(); diff --git a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/breakdown_filter.ts b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/breakdown_filter.ts deleted file mode 100644 index 5b4934eac1f71..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/breakdown_filter.ts +++ /dev/null @@ -1,43 +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 { Given, When, Then } from 'cypress-cucumber-preprocessor/steps'; -import { DEFAULT_TIMEOUT } from './csm_dashboard'; -import { waitForLoadingToFinish } from './utils'; - -/** The default time in ms to wait for a Cypress command to complete */ - -Given(`a user clicks the page load breakdown filter`, () => { - waitForLoadingToFinish(); - cy.get('.euiStat__title-isLoading').should('not.exist'); - const breakDownBtn = cy.get( - '[data-test-subj=pldBreakdownFilter]', - DEFAULT_TIMEOUT - ); - breakDownBtn.click(); -}); - -When(`the user selected the breakdown`, () => { - cy.get('[id="user_agent.name"]', DEFAULT_TIMEOUT).click(); - // click outside popover to close it - cy.get('[data-cy=pageLoadDist]').click(); -}); - -Then(`breakdown series should appear in chart`, () => { - cy.get('.euiLoadingChart').should('not.exist'); - - cy.get('[data-cy=pageLoadDist]').within(() => { - cy.get('button.echLegendItem__label[title=Chrome] ', DEFAULT_TIMEOUT) - .invoke('text') - .should('eq', 'Chrome'); - - cy.get('button.echLegendItem__label', DEFAULT_TIMEOUT).should( - 'have.text', - 'ChromeChrome Mobile WebViewSafariFirefoxMobile SafariChrome MobileChrome Mobile iOSOverall' - ); - }); -}); diff --git a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/client_metrics_helper.ts b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/client_metrics_helper.ts deleted file mode 100644 index 1ff6ccd423070..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/client_metrics_helper.ts +++ /dev/null @@ -1,36 +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 { DEFAULT_TIMEOUT } from './csm_dashboard'; -import { waitForLoadingToFinish } from './utils'; - -/** - * Verifies the behavior of the client metrics component - * @param metrics array of three elements - * @param checkTitleStatus if it's needed to check title elements - */ -export function verifyClientMetrics( - metrics: string[], - checkTitleStatus: boolean -) { - const clientMetricsSelector = '[data-cy=client-metrics] .euiStat__title'; - - waitForLoadingToFinish(); - - if (checkTitleStatus) { - cy.get('.euiStat__title', DEFAULT_TIMEOUT).should('be.visible'); - cy.get('.euiSelect-isLoading').should('not.exist'); - } - - cy.get('.euiStat__title-isLoading').should('not.exist'); - - cy.get(clientMetricsSelector).eq(0).should('have.text', metrics[0]); - - cy.get(clientMetricsSelector).eq(1).should('have.text', metrics[1]); - - cy.get(clientMetricsSelector).eq(2).should('have.text', metrics[2]); -} diff --git a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/csm_dashboard.ts b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/csm_dashboard.ts deleted file mode 100644 index 47154ee214dc4..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/csm_dashboard.ts +++ /dev/null @@ -1,77 +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 { Given, Then } from 'cypress-cucumber-preprocessor/steps'; -import { loginAndWaitForPage } from '../../../integration/helpers'; -import { verifyClientMetrics } from './client_metrics_helper'; -import { waitForLoadingToFinish } from './utils'; - -/** The default time in ms to wait for a Cypress command to complete */ -export const DEFAULT_TIMEOUT = { timeout: 60 * 1000 }; - -Given(`a user browses the APM UI application for RUM Data`, () => { - // Open UX landing page - const RANGE_FROM = 'now-24h'; - const RANGE_TO = 'now'; - loginAndWaitForPage( - `/app/ux`, - { - from: RANGE_FROM, - to: RANGE_TO, - }, - 'client' - ); -}); - -Then(`should have correct client metrics`, () => { - const metrics = ['80 ms', '4 ms', '76 ms', '55']; - - verifyClientMetrics(metrics, true); -}); - -Then(`should display percentile for page load chart`, () => { - const pMarkers = '[data-cy=percentile-markers] span'; - - cy.get('.euiLoadingChart', DEFAULT_TIMEOUT).should('be.visible'); - - waitForLoadingToFinish(); - - cy.get('.euiStat__title-isLoading').should('not.exist'); - - cy.get(pMarkers).eq(0).should('have.text', '50th'); - - cy.get(pMarkers).eq(1).should('have.text', '75th'); - - cy.get(pMarkers).eq(2).should('have.text', '90th'); - - cy.get(pMarkers).eq(3).should('have.text', '95th'); -}); - -Then(`should display chart legend`, () => { - const chartLegend = 'button.echLegendItem__label'; - - waitForLoadingToFinish(); - cy.get('.euiLoadingChart').should('not.exist'); - - cy.get('[data-cy=pageLoadDist]').within(() => { - cy.get(chartLegend, DEFAULT_TIMEOUT).eq(0).should('have.text', 'Overall'); - }); -}); - -Then(`should display tooltip on hover`, () => { - cy.get('.euiLoadingChart').should('not.exist'); - - const pMarkers = '[data-cy=percentile-markers] span.euiToolTipAnchor'; - - waitForLoadingToFinish(); - cy.get('.euiLoadingChart').should('not.exist'); - - const marker = cy.get(pMarkers, DEFAULT_TIMEOUT).eq(0); - marker.invoke('show'); - marker.trigger('mouseover', { force: true }); - cy.get('span[data-cy=percentileTooltipTitle]').should('be.visible'); -}); diff --git a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/csm_filters.ts b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/csm_filters.ts deleted file mode 100644 index 168976d96ddc7..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/csm_filters.ts +++ /dev/null @@ -1,65 +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 { When, Then } from 'cypress-cucumber-preprocessor/steps'; -import { DEFAULT_TIMEOUT } from './csm_dashboard'; -import { verifyClientMetrics } from './client_metrics_helper'; -import { waitForLoadingToFinish } from './utils'; - -When(/^the user filters by "([^"]*)"$/, (filterName) => { - waitForLoadingToFinish(); - cy.get('.euiStat__title-isLoading').should('not.exist'); - - cy.get( - `button[aria-label="expands filter group for ${filterName} filter"]` - ).click(); - - cy.get(`.euiPopover__panel-isOpen`, DEFAULT_TIMEOUT).within(() => { - if (filterName === 'OS') { - const osItem = cy.get('li.euiSelectableListItem', DEFAULT_TIMEOUT).eq(2); - osItem.should('have.text', 'Mac OS X24 '); - osItem.click(); - - // sometimes click doesn't work as expected so we need to retry here - osItem.invoke('attr', 'aria-selected').then((val) => { - if (val === 'false') { - cy.get('li.euiSelectableListItem', DEFAULT_TIMEOUT).eq(2).click(); - } - }); - } else { - const deItem = cy.get('li.euiSelectableListItem', DEFAULT_TIMEOUT).eq(0); - deItem.should('have.text', 'DE84 '); - deItem.click(); - - // sometimes click doesn't work as expected so we need to retry here - deItem.invoke('attr', 'aria-selected').then((val) => { - if (val === 'false') { - cy.get('li.euiSelectableListItem', DEFAULT_TIMEOUT).eq(0).click(); - } - }); - } - cy.contains('Apply').click(); - }); - - cy.get(`.globalFilterLabel__value`, DEFAULT_TIMEOUT).contains( - filterName === 'OS' ? 'Mac OS X' : 'DE' - ); -}); - -Then(/^it filters the client metrics "([^"]*)"$/, (filterName) => { - waitForLoadingToFinish(); - cy.get('.euiStat__title-isLoading').should('not.exist'); - - const data = - filterName === 'OS' - ? ['82 ms', '5 ms', '77 ms', '8'] - : ['75 ms', '4 ms', '71 ms', '28']; - - verifyClientMetrics(data, true); - - cy.get('[data-cy=clearFilters]', DEFAULT_TIMEOUT).click(); -}); diff --git a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/js_errors.ts b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/js_errors.ts deleted file mode 100644 index 6bffb96be7f4a..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/js_errors.ts +++ /dev/null @@ -1,24 +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 { Then } from 'cypress-cucumber-preprocessor/steps'; -import { DEFAULT_TIMEOUT } from './csm_dashboard'; -import { getDataTestSubj } from './utils'; - -Then(`it displays list of relevant js errors`, () => { - cy.get('.euiBasicTable-loading').should('not.exist'); - cy.get('.euiStat__title-isLoading').should('not.exist'); - - getDataTestSubj('uxJsErrorsTotal').should('have.text', 'Total errors112'); - - getDataTestSubj('uxJsErrorTable').within(() => { - cy.get('tr.euiTableRow', DEFAULT_TIMEOUT) - .eq(0) - .invoke('text') - .should('eq', 'Error messageTest CaptureErrorImpacted page loads100.0 %'); - }); -}); diff --git a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/percentile_select.ts b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/percentile_select.ts deleted file mode 100644 index 22e6210590192..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/percentile_select.ts +++ /dev/null @@ -1,22 +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 { When, Then } from 'cypress-cucumber-preprocessor/steps'; -import { verifyClientMetrics } from './client_metrics_helper'; -import { getDataTestSubj } from './utils'; - -When('the user changes the selected percentile', () => { - getDataTestSubj('uxPercentileSelect').select('95'); -}); - -Then(`it displays client metric related to that percentile`, () => { - const metrics = ['165 ms', '14 ms', '151 ms', '55']; - - verifyClientMetrics(metrics, false); - - getDataTestSubj('uxPercentileSelect').select('50'); -}); diff --git a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/service_name_filter.ts b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/service_name_filter.ts deleted file mode 100644 index 26c3fade5afaa..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/service_name_filter.ts +++ /dev/null @@ -1,22 +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 { When, Then } from 'cypress-cucumber-preprocessor/steps'; -import { verifyClientMetrics } from './client_metrics_helper'; -import { DEFAULT_TIMEOUT } from './csm_dashboard'; -import { waitForLoadingToFinish } from './utils'; - -When('the user changes the selected service name', () => { - waitForLoadingToFinish(); - cy.get(`[data-cy=serviceNameFilter]`, DEFAULT_TIMEOUT).select('client'); -}); - -Then(`it displays relevant client metrics`, () => { - const metrics = ['80 ms', '4 ms', '76 ms', '55']; - - verifyClientMetrics(metrics, false); -}); diff --git a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/url_search_filter.ts b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/url_search_filter.ts deleted file mode 100644 index a277ff5a9a136..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/url_search_filter.ts +++ /dev/null @@ -1,66 +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 { When, Then } from 'cypress-cucumber-preprocessor/steps'; -import { DEFAULT_TIMEOUT } from './csm_dashboard'; -import { waitForLoadingToFinish } from './utils'; - -When(`a user clicks inside url search field`, () => { - waitForLoadingToFinish(); - cy.get('.euiStat__title-isLoading').should('not.exist'); - cy.get('span[data-cy=csmUrlFilter]', DEFAULT_TIMEOUT).within(() => { - cy.get('input.euiFieldSearch').click(); - }); -}); - -Then(`it displays top pages in the suggestion popover`, () => { - waitForLoadingToFinish(); - - cy.get('div.euiPopover__panel-isOpen', DEFAULT_TIMEOUT).within(() => { - const listOfUrls = cy.get('li.euiSelectableListItem'); - listOfUrls.should('have.length', 5); - - const actualUrlsText = [ - 'http://opbeans-node:3000/dashboardTotal page views: 17Page load duration: 109 ms (median)', - 'http://opbeans-node:3000/ordersTotal page views: 14Page load duration: 72 ms (median)', - ]; - - cy.get('li.euiSelectableListItem') - .eq(0) - .should('have.text', actualUrlsText[0]); - cy.get('li.euiSelectableListItem') - .eq(1) - .should('have.text', actualUrlsText[1]); - }); -}); - -When(`a user enters a query in url search field`, () => { - waitForLoadingToFinish(); - - cy.get('[data-cy=csmUrlFilter]').within(() => { - cy.get('input.euiSelectableSearch').type('cus'); - }); - - waitForLoadingToFinish(); -}); - -Then(`it should filter results based on query`, () => { - waitForLoadingToFinish(); - - cy.get('div.euiPopover__panel-isOpen', DEFAULT_TIMEOUT).within(() => { - const listOfUrls = cy.get('li.euiSelectableListItem'); - listOfUrls.should('have.length', 1); - - const actualUrlsText = [ - 'http://opbeans-node:3000/customersTotal page views: 10Page load duration: 76 ms (median)', - ]; - - cy.get('li.euiSelectableListItem') - .eq(0) - .should('have.text', actualUrlsText[0]); - }); -}); diff --git a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/utils.ts b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/utils.ts deleted file mode 100644 index 962f36978d87b..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm/utils.ts +++ /dev/null @@ -1,18 +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 { DEFAULT_TIMEOUT } from './csm_dashboard'; - -export function waitForLoadingToFinish() { - cy.get('[data-test-subj=globalLoadingIndicator-hidden]', DEFAULT_TIMEOUT); -} - -export function getDataTestSubj(dataTestSubj: string) { - waitForLoadingToFinish(); - - return cy.get(`[data-test-subj=${dataTestSubj}]`, DEFAULT_TIMEOUT); -} diff --git a/x-pack/plugins/apm/e2e/cypress/typings/index.d.ts b/x-pack/plugins/apm/e2e/cypress/typings/index.d.ts deleted file mode 100644 index 256db47dc74fd..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/typings/index.d.ts +++ /dev/null @@ -1,12 +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. - */ - -declare namespace Cypress { - interface Chainable { - snapshot: () => {}; - } -} diff --git a/x-pack/plugins/apm/e2e/cypress/webpack.config.js b/x-pack/plugins/apm/e2e/cypress/webpack.config.js deleted file mode 100644 index afeb527cd6b08..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/webpack.config.js +++ /dev/null @@ -1,44 +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. - */ - -module.exports = { - resolve: { - extensions: ['.ts', '.js'], - symlinks: false, - }, - node: { fs: 'empty', child_process: 'empty', readline: 'empty' }, - module: { - rules: [ - { - test: /\.ts$/, - exclude: [/node_modules/], - include: [/e2e\/cypress/], - use: [ - { - loader: 'ts-loader', - }, - ], - }, - { - test: /\.feature$/, - use: [ - { - loader: 'cypress-cucumber-preprocessor/loader', - }, - ], - }, - { - test: /\.features$/, - use: [ - { - loader: 'cypress-cucumber-preprocessor/lib/featuresLoader', - }, - ], - }, - ], - }, -}; diff --git a/x-pack/plugins/apm/e2e/ingest-data/replay.js b/x-pack/plugins/apm/e2e/ingest-data/replay.js deleted file mode 100644 index 7cda2a8064f7f..0000000000000 --- a/x-pack/plugins/apm/e2e/ingest-data/replay.js +++ /dev/null @@ -1,173 +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. - */ - -/* eslint-disable no-console */ - -/* eslint-disable import/no-extraneous-dependencies */ - -/** - * This script is useful for ingesting previously generated APM data into Elasticsearch via APM Server - * - * You can either: - * 1. Download a static test data file from: https://storage.googleapis.com/apm-ui-e2e-static-data/events.json - * 2. Or, generate the test data file yourself: - * git clone https://github.com/elastic/apm-integration-testing.git - * ./scripts/compose.py start master --no-kibana --with-opbeans-node --apm-server-record - * docker cp localtesting_8.0.0_apm-server-2:/app/events.json . && cat events.json | wc -l - * - * - * - * Run the script: - * - * node replay.js --server-url --secret-token --events ./events.json - * - ************/ - -const { promisify } = require('util'); -const fs = require('fs'); -const path = require('path'); -const axios = require('axios'); -const readFile = promisify(fs.readFile); -const pLimit = require('p-limit'); -const pRetry = require('p-retry'); -const { argv } = require('yargs'); -const ora = require('ora'); -const userAgents = require('./user_agents'); -const userIps = require('./rum_ips'); - -const APM_SERVER_URL = argv.serverUrl; -const SECRET_TOKEN = argv.secretToken; -const EVENTS_PATH = argv.events; - -if (!APM_SERVER_URL) { - console.log('`--server-url` is required'); - process.exit(1); -} - -if (!EVENTS_PATH) { - console.log('`--events` is required'); - process.exit(1); -} - -const requestProgress = { - succeeded: 0, - failed: 0, - total: 0, -}; - -const spinner = ora({ text: 'Warming up...', stream: process.stdout }); - -function incrementSpinnerCount({ success }) { - success ? requestProgress.succeeded++ : requestProgress.failed++; - const remaining = - requestProgress.total - - (requestProgress.succeeded + requestProgress.failed); - - spinner.text = `Remaining: ${remaining}. Succeeded: ${requestProgress.succeeded}. Failed: ${requestProgress.failed}.`; -} -let iterIndex = 0; - -function setItemMetaAndHeaders(item) { - const headers = { - 'content-type': 'application/x-ndjson', - }; - - if (SECRET_TOKEN) { - headers.Authorization = `Bearer ${SECRET_TOKEN}`; - } - - if (item.url === '/intake/v2/rum/events') { - if (iterIndex === userAgents.length) { - // set some event agent to opbean - setRumAgent(item); - iterIndex = 0; - } - headers['User-Agent'] = userAgents[iterIndex]; - headers['X-Forwarded-For'] = userIps[iterIndex]; - iterIndex++; - } - return headers; -} - -function setRumAgent(item) { - if (item.body) { - item.body = item.body.replace( - '"name":"client"', - '"name":"elastic-frontend"' - ); - } -} - -async function insertItem(item, headers) { - try { - const url = `${APM_SERVER_URL}${item.url}`; - - await axios({ - method: item.method, - url, - headers, - data: item.body, - }); - } catch (e) { - console.error( - `${e.response ? JSON.stringify(e.response.data) : e.message}` - ); - throw e; - } -} - -async function init() { - const content = await readFile(path.resolve(EVENTS_PATH)); - const items = content - .toString() - .split('\n') - .filter((item) => item) - .map((item) => JSON.parse(item)) - .filter((item) => { - return ( - item.url === '/intake/v2/events' || item.url === '/intake/v2/rum/events' - ); - }); - - spinner.start(); - requestProgress.total = items.length; - - const limit = pLimit(20); // number of concurrent requests - await Promise.all( - items.map(async (item) => { - try { - const headers = setItemMetaAndHeaders(item); - // retry 5 times with exponential backoff - await pRetry(() => limit(() => insertItem(item, headers)), { - retries: 5, - }); - incrementSpinnerCount({ success: true }); - } catch (e) { - incrementSpinnerCount({ success: false }); - } - }) - ); -} - -init() - .then(() => { - if (requestProgress.succeeded === requestProgress.total) { - spinner.succeed( - `Successfully ingested ${requestProgress.succeeded} of ${requestProgress.total} events` - ); - process.exit(0); - } else { - spinner.fail( - `Ingested ${requestProgress.succeeded} of ${requestProgress.total} events` - ); - process.exit(1); - } - }) - .catch((e) => { - console.log('An error occurred:', e); - process.exit(1); - }); diff --git a/x-pack/plugins/apm/e2e/ingest-data/rum_ips.js b/x-pack/plugins/apm/e2e/ingest-data/rum_ips.js deleted file mode 100644 index 43bb01a7a23f5..0000000000000 --- a/x-pack/plugins/apm/e2e/ingest-data/rum_ips.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -const IPS = [ - '89.191.86.214', // check24.de - '167.40.79.24', // canada.ca - '151.101.130.217', // elastic.co - '185.143.68.17', - '151.101.130.217', - '185.143.68.17', - '185.143.68.17', - '151.101.130.217', - '185.143.68.17', -]; - -module.exports = IPS; diff --git a/x-pack/plugins/apm/e2e/ingest-data/user_agents.js b/x-pack/plugins/apm/e2e/ingest-data/user_agents.js deleted file mode 100644 index f8829875bc79e..0000000000000 --- a/x-pack/plugins/apm/e2e/ingest-data/user_agents.js +++ /dev/null @@ -1,24 +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. - */ - -/* eslint-disable no-console */ - -/* eslint-disable import/no-extraneous-dependencies */ - -const UserAgents = [ - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36', - 'Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36', - 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/69.0.3497.105 Mobile/15E148 Safari/605.1', - 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1', - 'Mozilla/5.0 (Linux; Android 7.0; Pixel C Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/52.0.2743.98 Safari/537.36', - 'Mozilla/5.0 (X11; CrOS x86_64 8172.45.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.64 Safari/537.36', - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9', - 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1', - 'Mozilla/5.0 (CrKey armv7l 1.5.16041) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.0 Safari/537.36', -]; - -module.exports = UserAgents; diff --git a/x-pack/plugins/apm/e2e/run-e2e.sh b/x-pack/plugins/apm/e2e/run-e2e.sh deleted file mode 100755 index 1b3afb4823426..0000000000000 --- a/x-pack/plugins/apm/e2e/run-e2e.sh +++ /dev/null @@ -1,176 +0,0 @@ -#!/usr/bin/env bash - -# variables -KIBANA_PORT=5701 -ELASTICSEARCH_PORT=9201 -APM_SERVER_PORT=8201 - -# ensure Docker is running -docker ps &> /dev/null -if [ $? -ne 0 ]; then - echo "⚠️ Please start Docker" - exit 1 -fi - -# formatting -bold=$(tput bold) -normal=$(tput sgr0) - -# paths -E2E_DIR="${0%/*}" -TMP_DIR="tmp" -APM_IT_DIR="tmp/apm-integration-testing" -WAIT_ON_BIN="../../../../node_modules/.bin/wait-on" -CYPRESS_BIN="../../../../node_modules/.bin/cypress" - -cd ${E2E_DIR} - -KIBANA_VERSION=$(node -p "require('../../../package.json').version") - -# -# Ask user to start Kibana -################################################## -echo "" # newline -echo "${bold}To start Kibana please run the following command:${normal} -node ./scripts/kibana --no-base-path --dev --no-dev-config --config x-pack/plugins/apm/e2e/ci/kibana.e2e.yml" - -# -# Create tmp folder -################################################## -echo "" # newline -echo "${bold}Temporary folder${normal}" -echo "Temporary files will be stored in: ${E2E_DIR}${TMP_DIR}" -mkdir -p ${TMP_DIR} - -# -# apm-integration-testing -################################################## -echo "" # newline -echo "${bold}apm-integration-testing (logs: ${E2E_DIR}${TMP_DIR}/apm-it.log)${normal}" - -# pull if folder already exists -if [ -d ${APM_IT_DIR} ]; then - echo "Pulling from master..." - git -C ${APM_IT_DIR} pull &> ${TMP_DIR}/apm-it.log - -# clone if folder does not exists -else - echo "Cloning repository" - git clone "https://github.com/elastic/apm-integration-testing.git" ${APM_IT_DIR} &> ${TMP_DIR}/apm-it.log -fi - -# Stop if clone/pull failed -if [ $? -ne 0 ]; then - echo "⚠️ Initializing apm-integration-testing failed." - exit 1 -fi - -# Start apm-integration-testing -echo "Starting docker-compose" -echo "Using stack version: ${KIBANA_VERSION}" -${APM_IT_DIR}/scripts/compose.py start $KIBANA_VERSION \ - --no-kibana \ - --elasticsearch-port $ELASTICSEARCH_PORT \ - --apm-server-port=$APM_SERVER_PORT \ - --elasticsearch-heap 4g \ - --apm-server-opt queue.mem.events=8192 \ - --apm-server-opt output.elasticsearch.bulk_max_size=4096 \ - &> ${TMP_DIR}/apm-it.log - -# Stop if apm-integration-testing failed to start correctly -if [ $? -ne 0 ]; then - echo "⚠️ apm-integration-testing could not be started" - echo "" # newline - echo "As a last resort, reset docker with:" - echo "" # newline - echo "cd ${E2E_DIR}${APM_IT_DIR} && scripts/compose.py stop && docker system prune --all --force --volumes" - echo "" # newline - - # output logs for excited docker containers - cd ${APM_IT_DIR} && docker-compose ps --filter "status=exited" -q | xargs -L1 docker logs --tail=10 && cd - - - echo "" # newline - echo "Find the full logs in ${E2E_DIR}${TMP_DIR}/apm-it.log" - exit 1 -fi - -# -# Static mock data -################################################## -echo "" # newline -echo "${bold}Static mock data (logs: ${E2E_DIR}${TMP_DIR}/ingest-data.log)${normal}" - -STATIC_MOCK_FILENAME='2020-06-12.json' - -# Download static data if not already done -if [ ! -e "${TMP_DIR}/${STATIC_MOCK_FILENAME}" ]; then - echo "Downloading ${STATIC_MOCK_FILENAME}..." - curl --silent https://storage.googleapis.com/apm-ui-e2e-static-data/${STATIC_MOCK_FILENAME} --output ${TMP_DIR}/${STATIC_MOCK_FILENAME} -fi - -# echo "Deleting existing indices (apm* and .apm*)" -curl --silent --user admin:changeme -XDELETE "localhost:${ELASTICSEARCH_PORT}/.apm*" > /dev/null -curl --silent --user admin:changeme -XDELETE "localhost:${ELASTICSEARCH_PORT}/apm*" > /dev/null - -# Ingest data into APM Server -node ingest-data/replay.js --server-url http://localhost:$APM_SERVER_PORT --events ${TMP_DIR}/${STATIC_MOCK_FILENAME} 2>> ${TMP_DIR}/ingest-data.log - -# Abort if not all events were ingested correctly -if [ $? -ne 0 ]; then - echo "⚠️ Not all events were ingested correctly. This might affect test tests." - echo "Aborting. Please try again." - echo "" # newline - echo "Full logs in ${E2E_DIR}${TMP_DIR}/ingest-data.log:" - - # output logs for excited docker containers - cd ${APM_IT_DIR} && docker-compose ps --filter "status=exited" -q | xargs -L1 docker logs --tail=3 && cd - - - # stop docker containers - cd ${APM_IT_DIR} && ./scripts/compose.py stop > /dev/null && cd - - exit 1 -fi - -# create empty snapshot file if it doesn't exist -SNAPSHOTS_FILE=cypress/integration/snapshots.js -if [ ! -f ${SNAPSHOTS_FILE} ]; then - echo "{}" > ${SNAPSHOTS_FILE} -fi - -# -# Wait for Kibana to start -################################################## -echo "" # newline -echo "${bold}Waiting for Kibana to start...${normal}" -echo "Note: you need to start Kibana manually. Find the instructions at the top." -$WAIT_ON_BIN -i 500 -w 500 http-get://admin:changeme@localhost:$KIBANA_PORT/api/status > /dev/null - -## Workaround to wait for the http server running -## See: https://github.com/elastic/kibana/issues/66326 -if [ -e kibana.log ] ; then - grep -m 1 "Kibana is now available" <(tail -f -n +1 kibana.log) - echo "✅ Kibana server running..." - grep -m 1 "bundles compiled successfully" <(tail -f -n +1 kibana.log) - echo "✅ Kibana bundles have been compiled..." -fi - - -echo "✅ Setup completed successfully. Running tests..." - -# -# run cypress tests -################################################## -$CYPRESS_BIN run --config pageLoadTimeout=100000,watchForFileChanges=true -e2e_status=$? - -# -# Run interactively -################################################## -echo "${bold}If you want to run the test interactively, run:${normal}" -echo "" # newline -echo "cd ${E2E_DIR} && ${CYPRESS_BIN} open --config pageLoadTimeout=100000,watchForFileChanges=true" - -# Report the e2e status at the very end -if [ $e2e_status -ne 0 ]; then - echo "⚠️ Running tests failed." - exit 1 -fi diff --git a/x-pack/plugins/apm/e2e/tsconfig.json b/x-pack/plugins/apm/e2e/tsconfig.json deleted file mode 100644 index 2560a15df9224..0000000000000 --- a/x-pack/plugins/apm/e2e/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../../../tsconfig.base.json", - "include": ["**/*"], - "exclude": ["tmp", "target/**/*"], - "compilerOptions": { - "outDir": "target/types", - "types": ["cypress", "node"] - } -} diff --git a/x-pack/plugins/apm/jest.config.js b/x-pack/plugins/apm/jest.config.js index 77639cb58d497..4fd2e72776943 100644 --- a/x-pack/plugins/apm/jest.config.js +++ b/x-pack/plugins/apm/jest.config.js @@ -12,7 +12,6 @@ module.exports = { rootDir: path.resolve(__dirname, '../../..'), roots: ['/x-pack/plugins/apm'], setupFiles: ['/x-pack/plugins/apm/.storybook/jest_setup.js'], - testPathIgnorePatterns: ['/x-pack/plugins/apm/e2e/'], coverageDirectory: '/target/kibana-coverage/jest/x-pack/plugins/apm', coverageReporters: ['text', 'html'], collectCoverageFrom: [ diff --git a/x-pack/plugins/apm/scripts/optimize-tsconfig/tsconfig.json b/x-pack/plugins/apm/scripts/optimize-tsconfig/tsconfig.json index 40d42298b967b..31f756eeabde3 100644 --- a/x-pack/plugins/apm/scripts/optimize-tsconfig/tsconfig.json +++ b/x-pack/plugins/apm/scripts/optimize-tsconfig/tsconfig.json @@ -7,7 +7,6 @@ ], "exclude": [ "**/__fixtures__/**/*", - "./x-pack/plugins/apm/e2e", "./x-pack/plugins/apm/ftr_e2e" ], "compilerOptions": { From d6e2080b62d2b3022ffc36a10e2f668126eac563 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 16 Sep 2021 08:22:44 -0400 Subject: [PATCH 5/8] fixed big header (#112380) (#112393) Co-authored-by: juliaElastic <90178898+juliaElastic@users.noreply.github.com> --- .../sections/epm/screens/detail/index.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx index 26869f8fea574..f0630f89c8984 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx @@ -79,6 +79,13 @@ const FlexItemWithMinWidth = styled(EuiFlexItem)` min-width: 0px; `; +// to limit size of iconpanel, making the header too big +const FlexItemWithMaxHeight = styled(EuiFlexItem)` + @media (min-width: 768px) { + max-height: 60px; + } +`; + function Breadcrumbs({ packageTitle }: { packageTitle: string }) { useBreadcrumbs('integration_details_overview', { pkgTitle: packageTitle }); return null; @@ -173,7 +180,7 @@ export function Detail() { - + {isLoading || !packageInfo ? ( ) : ( @@ -184,7 +191,7 @@ export function Detail() { icons={integrationInfo?.icons || packageInfo.icons} /> )} - + From 9f0fd25cf406d7068e816adda333abba49b4a43e Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 16 Sep 2021 08:24:51 -0400 Subject: [PATCH 6/8] [APM] Remove new badge from Backends link in the solution navigation (#112269) (#112394) Co-authored-by: Miriam <31922082+MiriamAparicio@users.noreply.github.com> --- x-pack/plugins/apm/public/plugin.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index da2ea0ba8ae5c..1e1d9ce5687d1 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -132,7 +132,6 @@ export class ApmPlugin implements Plugin { label: dependenciesTitle, app: 'apm', path: '/backends', - isNewFeature: true, onClick: () => { const { usageCollection } = pluginsStart as { usageCollection?: UsageCollectionStart; From e3f344f053f4f48378dcb856ca90d25cd7407ee9 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 16 Sep 2021 09:39:27 -0400 Subject: [PATCH 7/8] [Uptime] For filters, use shared component (#105739) (#112404) Co-authored-by: Justin Kambic Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Shahzad Co-authored-by: Justin Kambic --- .../field_value_selection.tsx | 12 +- .../shared/field_value_suggestions/index.tsx | 4 + .../shared/field_value_suggestions/types.ts | 3 + .../public/hooks/use_values_list.ts | 33 +++- .../translations/translations/ja-JP.json | 5 - .../translations/translations/zh-CN.json | 5 - .../uptime/common/constants/rest_api.ts | 2 - x-pack/plugins/uptime/common/constants/ui.ts | 7 + .../uptime/common/runtime_types/index.ts | 1 - .../runtime_types/overview_filters/index.ts | 8 - .../overview_filters/overview_filters.ts | 17 -- .../uptime/common/runtime_types/ping/ping.ts | 1 + .../plugins/uptime/public/apps/uptime_app.tsx | 25 +-- .../public/components/common/monitor_tags.tsx | 13 +- .../common/uptime_date_picker.test.tsx | 2 +- .../monitor/ping_list/ping_list_header.tsx | 4 +- .../components/monitor/ping_list/use_pings.ts | 11 +- .../alerts/alert_query_bar/query_bar.tsx | 4 +- .../alert_monitor_status.tsx | 37 ++--- .../alerts/alerts_containers/use_snap_shot.ts | 5 +- .../availability_expression_select.tsx | 8 +- .../filters_expression_select.test.tsx | 114 +++----------- .../filters_expression_select.tsx | 145 +++++++++-------- .../filters_expression_select_container.tsx | 25 --- .../alerts/monitor_expressions/index.ts | 1 - .../alert_monitor_status.test.tsx | 40 ++++- .../alert_monitor_status.tsx | 26 ++-- .../overview/alerts/translations.ts | 9 ++ .../parse_filter_map.test.ts.snap | 20 --- .../filter_group/filter_group.test.tsx | 116 +++++++++----- .../overview/filter_group/filter_group.tsx | 122 ++++++--------- .../filter_group/filter_group_container.tsx | 57 ------- .../filter_group/filter_popover.test.tsx | 96 ------------ .../overview/filter_group/filter_popover.tsx | 146 ------------------ .../components/overview/filter_group/index.ts | 9 -- .../filter_group/parse_filter_map.test.ts | 22 --- .../overview/filter_group/parse_filter_map.ts | 39 ----- .../filter_group/selected_filters.tsx | 79 ++++++++++ .../filter_group/toggle_selected_item.test.ts | 24 --- .../filter_group/toggle_selected_item.ts | 23 --- .../filter_group/uptime_filter_button.tsx | 44 ------ .../public/components/overview/index.ts | 1 - .../monitor_list/columns/monitor_name_col.tsx | 9 +- .../overview/query_bar/query_bar.tsx | 5 +- .../overview/query_bar/use_index_pattern.ts | 25 --- .../overview/query_bar/use_query_bar.test.tsx | 126 +++++++++++++++ .../overview/query_bar/use_query_bar.ts | 30 ++-- .../contexts/uptime_index_pattern_context.tsx | 42 +++++ .../use_url_params.test.tsx.snap | 4 +- x-pack/plugins/uptime/public/hooks/index.ts | 1 + .../public/hooks/update_kuery_string.ts | 27 +++- .../public/hooks/use_filter_update.test.ts | 32 ++++ .../uptime/public/hooks/use_filter_update.ts | 87 +++++++---- .../hooks/use_selected_filters.test.tsx | 43 ++++++ .../public/hooks/use_selected_filters.ts | 98 ++++++++++-- .../public/hooks/use_url_params.test.tsx | 4 +- .../uptime/public/hooks/use_url_params.ts | 62 ++++---- .../public/lib/__mocks__/uptime_store.mock.ts | 15 -- .../lazy_wrapper/monitor_status.tsx | 5 +- .../get_supported_url_params.test.ts.snap | 5 + .../get_supported_url_params.test.ts | 1 + .../url_params/get_supported_url_params.ts | 3 + .../parse_autorefresh_interval.test.ts | 25 --- .../plugins/uptime/public/pages/overview.tsx | 5 +- .../overview_filters.test.ts.snap | 62 -------- .../uptime/public/state/actions/index.ts | 2 - .../public/state/actions/index_pattern.ts | 13 -- .../state/actions/overview_filters.test.ts | 46 ------ .../public/state/actions/overview_filters.ts | 32 ---- .../public/state/actions/selected_filters.ts | 2 +- .../plugins/uptime/public/state/api/index.ts | 2 - .../uptime/public/state/api/index_pattern.ts | 13 -- .../public/state/api/overview_filters.ts | 33 ---- .../uptime/public/state/effects/index.ts | 4 - .../public/state/effects/index_pattern.ts | 18 --- .../public/state/effects/overview_filters.ts | 22 --- .../uptime/public/state/reducers/index.ts | 4 - .../public/state/reducers/index_pattern.ts | 44 ------ .../public/state/reducers/overview_filters.ts | 55 ------- .../state/reducers/selected_filters.test.ts | 8 +- .../public/state/selectors/index.test.ts | 17 +- .../uptime/public/state/selectors/index.ts | 10 -- .../combine_range_with_filters.test.ts | 111 ------------- .../server/lib/requests/get_filter_bar.ts | 86 ----------- .../server/lib/requests/get_pings.test.ts | 45 ++++++ .../uptime/server/lib/requests/get_pings.ts | 24 +++ .../uptime/server/lib/requests/index.ts | 2 - .../plugins/uptime/server/rest_api/index.ts | 5 +- .../rest_api/index_state/get_index_pattern.ts | 21 --- .../server/rest_api/index_state/index.ts | 1 - .../overview_filters/get_overview_filters.ts | 58 ------- .../server/rest_api/overview_filters/index.ts | 8 - .../uptime/server/rest_api/pings/get_pings.ts | 14 +- x-pack/test/functional/apps/uptime/monitor.ts | 2 +- .../test/functional/apps/uptime/overview.ts | 6 +- .../test/functional/services/uptime/alerts.ts | 17 +- .../test/functional/services/uptime/common.ts | 27 +++- .../apps/uptime/alert_flyout.ts | 14 +- 98 files changed, 1080 insertions(+), 1767 deletions(-) delete mode 100644 x-pack/plugins/uptime/common/runtime_types/overview_filters/index.ts delete mode 100644 x-pack/plugins/uptime/common/runtime_types/overview_filters/overview_filters.ts delete mode 100644 x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select_container.tsx delete mode 100644 x-pack/plugins/uptime/public/components/overview/filter_group/__snapshots__/parse_filter_map.test.ts.snap delete mode 100644 x-pack/plugins/uptime/public/components/overview/filter_group/filter_group_container.tsx delete mode 100644 x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.test.tsx delete mode 100644 x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.tsx delete mode 100644 x-pack/plugins/uptime/public/components/overview/filter_group/index.ts delete mode 100644 x-pack/plugins/uptime/public/components/overview/filter_group/parse_filter_map.test.ts delete mode 100644 x-pack/plugins/uptime/public/components/overview/filter_group/parse_filter_map.ts create mode 100644 x-pack/plugins/uptime/public/components/overview/filter_group/selected_filters.tsx delete mode 100644 x-pack/plugins/uptime/public/components/overview/filter_group/toggle_selected_item.test.ts delete mode 100644 x-pack/plugins/uptime/public/components/overview/filter_group/toggle_selected_item.ts delete mode 100644 x-pack/plugins/uptime/public/components/overview/filter_group/uptime_filter_button.tsx delete mode 100644 x-pack/plugins/uptime/public/components/overview/query_bar/use_index_pattern.ts create mode 100644 x-pack/plugins/uptime/public/components/overview/query_bar/use_query_bar.test.tsx create mode 100644 x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx create mode 100644 x-pack/plugins/uptime/public/hooks/use_filter_update.test.ts create mode 100644 x-pack/plugins/uptime/public/hooks/use_selected_filters.test.tsx delete mode 100644 x-pack/plugins/uptime/public/lib/helper/url_params/parse_autorefresh_interval.test.ts delete mode 100644 x-pack/plugins/uptime/public/state/actions/__snapshots__/overview_filters.test.ts.snap delete mode 100644 x-pack/plugins/uptime/public/state/actions/index_pattern.ts delete mode 100644 x-pack/plugins/uptime/public/state/actions/overview_filters.test.ts delete mode 100644 x-pack/plugins/uptime/public/state/actions/overview_filters.ts delete mode 100644 x-pack/plugins/uptime/public/state/api/index_pattern.ts delete mode 100644 x-pack/plugins/uptime/public/state/api/overview_filters.ts delete mode 100644 x-pack/plugins/uptime/public/state/effects/index_pattern.ts delete mode 100644 x-pack/plugins/uptime/public/state/effects/overview_filters.ts delete mode 100644 x-pack/plugins/uptime/public/state/reducers/index_pattern.ts delete mode 100644 x-pack/plugins/uptime/public/state/reducers/overview_filters.ts delete mode 100644 x-pack/plugins/uptime/server/lib/requests/combine_range_with_filters.test.ts delete mode 100644 x-pack/plugins/uptime/server/lib/requests/get_filter_bar.ts delete mode 100644 x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts delete mode 100644 x-pack/plugins/uptime/server/rest_api/overview_filters/get_overview_filters.ts delete mode 100644 x-pack/plugins/uptime/server/rest_api/overview_filters/index.ts diff --git a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_selection.tsx b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_selection.tsx index f713af9768229..aca29c4723688 100644 --- a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_selection.tsx +++ b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_selection.tsx @@ -71,6 +71,7 @@ export function FieldValueSelection({ selectedValue, excludedValue, compressed = true, + allowExclusions = true, onChange: onSelectionChange, }: FieldValueSelectionProps) { const [options, setOptions] = useState(() => @@ -142,7 +143,7 @@ export function FieldValueSelection({ .filter((opt) => opt?.checked === 'off') .map(({ label: labelN }) => labelN); - return isEqual(selectedValue ?? [], currSelected) && isEqual(excludedValue, currExcluded); + return isEqual(selectedValue ?? [], currSelected) && isEqual(excludedValue ?? [], currExcluded); }; return ( @@ -174,7 +175,7 @@ export function FieldValueSelection({ options={options} onChange={onChange} isLoading={loading && !query && options.length === 0} - allowExclusions={true} + allowExclusions={allowExclusions} > {(list, search) => (
@@ -190,6 +191,13 @@ export function FieldValueSelection({ )} ); } diff --git a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/types.ts b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/types.ts index d857b39b074ac..046f98748cdf2 100644 --- a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/types.ts +++ b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/types.ts @@ -24,6 +24,9 @@ interface CommonProps { asFilterButton?: boolean; showCount?: boolean; allowAllValuesSelection?: boolean; + cardinalityField?: string; + required?: boolean; + allowExclusions?: boolean; } export type FieldValueSuggestionsProps = CommonProps & { diff --git a/x-pack/plugins/observability/public/hooks/use_values_list.ts b/x-pack/plugins/observability/public/hooks/use_values_list.ts index 46d89a062f072..5aa7dd672cfda 100644 --- a/x-pack/plugins/observability/public/hooks/use_values_list.ts +++ b/x-pack/plugins/observability/public/hooks/use_values_list.ts @@ -18,6 +18,7 @@ export interface Props { filters?: ESFilter[]; time?: { from: string; to: string }; keepHistory?: boolean; + cardinalityField?: string; } export interface ListItem { @@ -32,6 +33,7 @@ export const useValuesList = ({ filters, time, keepHistory, + cardinalityField, }: Props): { values: ListItem[]; loading?: boolean } => { const [debouncedQuery, setDebounceQuery] = useState(query); const [values, setValues] = useState([]); @@ -93,9 +95,20 @@ export const useValuesList = ({ values: { terms: { field: sourceField, - size: 100, + size: 50, ...(query ? { include: includeClause } : {}), }, + ...(cardinalityField + ? { + aggs: { + count: { + cardinality: { + field: cardinalityField, + }, + }, + }, + } + : {}), }, }, }, @@ -105,10 +118,20 @@ export const useValuesList = ({ useEffect(() => { const newValues = - data?.aggregations?.values.buckets.map(({ key: value, doc_count: count }) => ({ - count, - label: String(value), - })) ?? []; + data?.aggregations?.values.buckets.map( + ({ key: value, doc_count: count, count: aggsCount }) => { + if (aggsCount) { + return { + count: aggsCount.value, + label: String(value), + }; + } + return { + count, + label: String(value), + }; + } + ) ?? []; if (keepHistory && query) { setValues((prevState) => { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index b01af66bf355b..374cca1618f78 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -26242,10 +26242,6 @@ "xpack.uptime.filterBar.options.portLabel": "ポート", "xpack.uptime.filterBar.options.schemeLabel": "スキーム", "xpack.uptime.filterBar.options.tagsLabel": "タグ", - "xpack.uptime.filterPopout.loadingMessage": "読み込み中...", - "xpack.uptime.filterPopout.searchMessage": "{title} の検索", - "xpack.uptime.filterPopout.searchMessage.ariaLabel": "{title} を検索", - "xpack.uptime.filterPopover.filterItem.label": "{title} {item}でフィルタリングします。", "xpack.uptime.fleetIntegration.assets.description": "アップタイムでモニターを表示", "xpack.uptime.fleetIntegration.assets.name": "監視", "xpack.uptime.integrationLink.missingDataMessage": "この統合に必要なデータが見つかりませんでした。", @@ -26396,7 +26392,6 @@ "xpack.uptime.overview.alerts.enabled.failed": "ルールを有効にできません。", "xpack.uptime.overview.alerts.enabled.success": "ルールが正常に有効にされました。 ", "xpack.uptime.overview.alerts.enabled.success.description": "この監視が停止しているときには、メッセージが {actionConnectors} に送信されます。", - "xpack.uptime.overview.filterButton.label": "{title}フィルターのフィルターグループを展開", "xpack.uptime.overview.heading": "監視", "xpack.uptime.overview.pageHeader.syntheticsCallout.announcementLink": "お知らせを読む", "xpack.uptime.overview.pageHeader.syntheticsCallout.content": "アップタイムは、スクリプト化された複数ステップの可用性チェックのサポートをプレビューしています。つまり、単に単一のページのアップ/ダウンのチェックだけではなく、Webページの要素を操作したり、全体的な可用性を確認したりできます(購入やシステムへのサインインなど)。詳細については以下をクリックしてください。これらの機能を先駆けて使用したい場合は、プレビュー合成エージェントをダウンロードし、アップタイムでチェックを表示できます。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 545c5d2965bef..92e8df98c9443 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -26677,10 +26677,6 @@ "xpack.uptime.filterBar.options.portLabel": "端口", "xpack.uptime.filterBar.options.schemeLabel": "方案", "xpack.uptime.filterBar.options.tagsLabel": "标签", - "xpack.uptime.filterPopout.loadingMessage": "正在加载……", - "xpack.uptime.filterPopout.searchMessage": "搜索 {title}", - "xpack.uptime.filterPopout.searchMessage.ariaLabel": "搜索 {title}", - "xpack.uptime.filterPopover.filterItem.label": "按 {title} {item} 筛选。", "xpack.uptime.fleetIntegration.assets.description": "在 Uptime 中查看监测", "xpack.uptime.fleetIntegration.assets.name": "监测", "xpack.uptime.integrationLink.missingDataMessage": "未找到此集成的所需数据。", @@ -26831,7 +26827,6 @@ "xpack.uptime.overview.alerts.enabled.failed": "无法启用规则!", "xpack.uptime.overview.alerts.enabled.success": "已成功启用规则 ", "xpack.uptime.overview.alerts.enabled.success.description": "此监测关闭时,将有消息发送到 {actionConnectors}。", - "xpack.uptime.overview.filterButton.label": "展开筛选 {title} 的筛选组", "xpack.uptime.overview.heading": "监测", "xpack.uptime.overview.pageHeader.syntheticsCallout.announcementLink": "阅读公告", "xpack.uptime.overview.pageHeader.syntheticsCallout.content": "Uptime 现在正在预览对脚本化多步骤可用性检查的支持。这意味着您可以与网页元素进行交互,并检查整个过程(例如购买或登录系统)的可用性,而不仅仅是简单的单个页面启动/关闭检查。请单击下面的内容以了解详情,如果您想率先使用这些功能,则可以下载我们的预览组合代理,并在 Uptime 中查看组合检查。", diff --git a/x-pack/plugins/uptime/common/constants/rest_api.ts b/x-pack/plugins/uptime/common/constants/rest_api.ts index 655f9629b848b..52b0620586eb4 100644 --- a/x-pack/plugins/uptime/common/constants/rest_api.ts +++ b/x-pack/plugins/uptime/common/constants/rest_api.ts @@ -6,7 +6,6 @@ */ export enum API_URLS { - INDEX_PATTERN = `/api/uptime/index_pattern`, INDEX_STATUS = '/api/uptime/index_status', MONITOR_LIST = `/api/uptime/monitor/list`, MONITOR_LOCATIONS = `/api/uptime/monitor/locations`, @@ -16,7 +15,6 @@ export enum API_URLS { PINGS = '/api/uptime/pings', PING_HISTOGRAM = `/api/uptime/ping/histogram`, SNAPSHOT_COUNT = `/api/uptime/snapshot/count`, - FILTERS = `/api/uptime/filters`, LOG_PAGE_VIEW = `/api/uptime/log_page_view`, ML_MODULE_JOBS = `/api/ml/modules/jobs_exist/`, diff --git a/x-pack/plugins/uptime/common/constants/ui.ts b/x-pack/plugins/uptime/common/constants/ui.ts index dcaf4bb310ad7..29df2614d0617 100644 --- a/x-pack/plugins/uptime/common/constants/ui.ts +++ b/x-pack/plugins/uptime/common/constants/ui.ts @@ -67,3 +67,10 @@ export enum CERT_STATUS { } export const KQL_SYNTAX_LOCAL_STORAGE = 'xpack.uptime.kql.syntax'; + +export const FILTER_FIELDS = { + TAGS: 'tags', + PORT: 'url.port', + LOCATION: 'observer.geo.name', + TYPE: 'monitor.type', +}; diff --git a/x-pack/plugins/uptime/common/runtime_types/index.ts b/x-pack/plugins/uptime/common/runtime_types/index.ts index 51dacd2f4e9b6..1c1b05cddcd44 100644 --- a/x-pack/plugins/uptime/common/runtime_types/index.ts +++ b/x-pack/plugins/uptime/common/runtime_types/index.ts @@ -10,7 +10,6 @@ export * from './certs'; export * from './common'; export * from './dynamic_settings'; export * from './monitor'; -export * from './overview_filters'; export * from './ping'; export * from './snapshot'; export * from './network_events'; diff --git a/x-pack/plugins/uptime/common/runtime_types/overview_filters/index.ts b/x-pack/plugins/uptime/common/runtime_types/overview_filters/index.ts deleted file mode 100644 index ed97b8902c879..0000000000000 --- a/x-pack/plugins/uptime/common/runtime_types/overview_filters/index.ts +++ /dev/null @@ -1,8 +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. - */ - -export { OverviewFiltersType, OverviewFilters } from './overview_filters'; diff --git a/x-pack/plugins/uptime/common/runtime_types/overview_filters/overview_filters.ts b/x-pack/plugins/uptime/common/runtime_types/overview_filters/overview_filters.ts deleted file mode 100644 index e3610a98f5ceb..0000000000000 --- a/x-pack/plugins/uptime/common/runtime_types/overview_filters/overview_filters.ts +++ /dev/null @@ -1,17 +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 * as t from 'io-ts'; - -export const OverviewFiltersType = t.partial({ - locations: t.array(t.string), - ports: t.array(t.number), - schemes: t.array(t.string), - tags: t.array(t.string), -}); - -export type OverviewFilters = t.TypeOf; diff --git a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts index d6875840a138c..d71e720f50e6e 100644 --- a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts +++ b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts @@ -304,6 +304,7 @@ export const GetPingsParamsType = t.intersection([ dateRange: DateRangeType, }), t.partial({ + excludedLocations: t.string, index: t.number, size: t.number, locations: t.string, diff --git a/x-pack/plugins/uptime/public/apps/uptime_app.tsx b/x-pack/plugins/uptime/public/apps/uptime_app.tsx index b31dd068ebb08..f82a312ef91f5 100644 --- a/x-pack/plugins/uptime/public/apps/uptime_app.tsx +++ b/x-pack/plugins/uptime/public/apps/uptime_app.tsx @@ -32,6 +32,7 @@ import { kibanaService } from '../state/kibana_service'; import { ActionMenu } from '../components/common/header/action_menu'; import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common'; import { Storage } from '../../../../../src/plugins/kibana_utils/public'; +import { UptimeIndexPatternContextProvider } from '../contexts/uptime_index_pattern_context'; export interface UptimeAppColors { danger: string; @@ -119,16 +120,20 @@ const Application = (props: UptimeAppProps) => { -
- - - - - -
+ +
+ +
+ + + +
+
+
+
diff --git a/x-pack/plugins/uptime/public/components/common/monitor_tags.tsx b/x-pack/plugins/uptime/public/components/common/monitor_tags.tsx index 892271f9ef8c5..793c27a031546 100644 --- a/x-pack/plugins/uptime/public/components/common/monitor_tags.tsx +++ b/x-pack/plugins/uptime/public/components/common/monitor_tags.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { EuiBadge, EuiBadgeGroup, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useHistory } from 'react-router-dom'; @@ -69,9 +69,14 @@ export const MonitorTags = ({ ping, summary }: Props) => { const currFilters = parseCurrentFilters(params.filters); - const [filterType, setFilterType] = useState(currFilters.get('tags') ?? []); + const [tagFilters, setTagFilters] = useState(currFilters.get('tags') ?? []); - useFilterUpdate('tags', filterType); + const excludedTagFilters = useMemo(() => { + const currExcludedFilters = parseCurrentFilters(params.excludedFilters); + return currExcludedFilters.get('tags') ?? []; + }, [params.excludedFilters]); + + useFilterUpdate('tags', tagFilters, excludedTagFilters); if (tags.length === 0) { return summary ? null : ( @@ -93,7 +98,7 @@ export const MonitorTags = ({ ping, summary }: Props) => { key={tag} title={getFilterLabel(tag)} onClick={() => { - setFilterType([tag]); + setTagFilters([tag]); }} onClickAriaLabel={getFilterLabel(tag)} color="hollow" diff --git a/x-pack/plugins/uptime/public/components/common/uptime_date_picker.test.tsx b/x-pack/plugins/uptime/public/components/common/uptime_date_picker.test.tsx index 4bfe7de33cba5..51909527c51e2 100644 --- a/x-pack/plugins/uptime/public/components/common/uptime_date_picker.test.tsx +++ b/x-pack/plugins/uptime/public/components/common/uptime_date_picker.test.tsx @@ -56,7 +56,7 @@ describe('UptimeDatePicker component', () => { expect(customHistory.push).toHaveBeenCalledWith({ pathname: '/', - search: 'dateRangeStart=now-30m&dateRangeEnd=now-15m', + search: 'dateRangeEnd=now-15m&dateRangeStart=now-30m', }); }); diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list_header.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list_header.tsx index 8e599cba6e97e..0284211d6259c 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list_header.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list_header.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; import { StatusFilter } from '../../overview/monitor_list/status_filter'; -import { FilterGroup } from '../../overview/filter_group'; +import { FilterGroup } from '../../overview/filter_group/filter_group'; export const PingListHeader = () => { return ( @@ -27,7 +27,7 @@ export const PingListHeader = () => { - + diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/use_pings.ts b/x-pack/plugins/uptime/public/components/monitor/ping_list/use_pings.ts index 9a8b1aa0e46f4..7e62b087ae671 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/use_pings.ts +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/use_pings.ts @@ -35,7 +35,7 @@ export const usePingsList = ({ pageSize, pageIndex }: Props) => { const { statusFilter } = useGetUrlParams(); - const { selectedLocations } = useSelectedFilters(); + const selectedFilters = useSelectedFilters(); const dispatch = useDispatch(); @@ -45,6 +45,9 @@ export const usePingsList = ({ pageSize, pageIndex }: Props) => { dispatch, ]); + const locations = JSON.stringify(selectedFilters.selectedLocations); + const excludedLocations = JSON.stringify(selectedFilters.excludedLocations); + useEffect(() => { getPings({ monitorId, @@ -52,7 +55,8 @@ export const usePingsList = ({ pageSize, pageIndex }: Props) => { from, to, }, - locations: JSON.stringify(selectedLocations), + excludedLocations, + locations, index: pageIndex, size: pageSize, status: statusFilter !== 'all' ? statusFilter : '', @@ -66,7 +70,8 @@ export const usePingsList = ({ pageSize, pageIndex }: Props) => { pageIndex, pageSize, statusFilter, - selectedLocations, + locations, + excludedLocations, ]); const { data } = useFetcher(() => { diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/alert_query_bar/query_bar.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/alert_query_bar/query_bar.tsx index 0a0bbadb6216f..4c6072a018642 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/alert_query_bar/query_bar.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/alert_query_bar/query_bar.tsx @@ -9,9 +9,9 @@ import React, { useEffect, useState } from 'react'; import { EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { QueryStringInput } from '../../../../../../../../src/plugins/data/public'; -import { useIndexPattern } from '../../query_bar/use_index_pattern'; import { isValidKuery } from '../../query_bar/query_bar'; import * as labels from '../translations'; +import { useIndexPattern } from '../../../../hooks'; interface Props { query: string; @@ -19,7 +19,7 @@ interface Props { } export const AlertQueryBar = ({ query = '', onChange }: Props) => { - const { index_pattern: indexPattern } = useIndexPattern(); + const indexPattern = useIndexPattern(); const [inputVal, setInputVal] = useState(query); diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx index ff2ef4d2359a8..39d19464caa2e 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx @@ -8,15 +8,18 @@ import React, { useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { isRight } from 'fp-ts/lib/Either'; -import { overviewFiltersSelector, selectedFiltersSelector } from '../../../../state/selectors'; +import { selectedFiltersSelector } from '../../../../state/selectors'; import { AlertMonitorStatusComponent } from '../monitor_status_alert/alert_monitor_status'; -import { fetchOverviewFilters, setSearchTextAction } from '../../../../state/actions'; +import { setSearchTextAction } from '../../../../state/actions'; import { AtomicStatusCheckParamsType, GetMonitorAvailabilityParamsType, } from '../../../../../common/runtime_types'; import { useSnapShotCount } from './use_snap_shot'; +import { FILTER_FIELDS } from '../../../../../common/constants'; + +const { TYPE, TAGS, LOCATION, PORT } = FILTER_FIELDS; interface Props { alertParams: { [key: string]: any }; @@ -37,23 +40,6 @@ export const AlertMonitorStatus: React.FC = ({ alertParams, }) => { const dispatch = useDispatch(); - useEffect(() => { - if (!window.location.pathname.includes('/app/uptime')) { - // filters inside uptime app already loaded - dispatch( - fetchOverviewFilters({ - dateRangeStart: 'now-24h', - dateRangeEnd: 'now', - locations: alertParams.filters?.['observer.geo.name'] ?? [], - ports: alertParams.filters?.['url.port'] ?? [], - tags: alertParams.filters?.tags ?? [], - schemes: alertParams.filters?.['monitor.type'] ?? [], - }) - ); - } - }, [alertParams, dispatch]); - - const overviewFilters = useSelector(overviewFiltersSelector); useEffect(() => { if (alertParams.search) { @@ -78,14 +64,10 @@ export const AlertMonitorStatus: React.FC = ({ useEffect(() => { if (!alertParams.filters && selectedFilters !== null) { setAlertParams('filters', { - // @ts-ignore - 'url.port': selectedFilters?.ports ?? [], - // @ts-ignore - 'observer.geo.name': selectedFilters?.locations ?? [], - // @ts-ignore - 'monitor.type': selectedFilters?.schemes ?? [], - // @ts-ignore - tags: selectedFilters?.tags ?? [], + [PORT]: selectedFilters?.ports ?? [], + [LOCATION]: selectedFilters?.locations ?? [], + [TYPE]: selectedFilters?.schemes ?? [], + [TAGS]: selectedFilters?.tags ?? [], }); } }, [alertParams, setAlertParams, selectedFilters]); @@ -94,7 +76,6 @@ export const AlertMonitorStatus: React.FC = ({ void; + hasFilters: boolean; } const TimeRangeOptions: TimeRangeOption[] = [ @@ -55,6 +56,7 @@ export const AvailabilityExpressionSelect: React.FC = ({ alertParams, isOldAlert, setAlertParams, + hasFilters, }) => { const [range, setRange] = useState(alertParams?.availability?.range ?? DEFAULT_RANGE); const [rangeUnit, setRangeUnit] = useState( @@ -114,7 +116,11 @@ export const AvailabilityExpressionSelect: React.FC = ({ /> } data-test-subj="xpack.uptime.alerts.monitorStatus.availability.threshold" - description={labels.ENTER_AVAILABILITY_THRESHOLD_DESCRIPTION} + description={ + hasFilters + ? labels.ENTER_AVAILABILITY_THRESHOLD_DESCRIPTION + : labels.ENTER_ANY_AVAILABILITY_THRESHOLD_DESCRIPTION + } id="threshold" isEnabled={isEnabled} isInvalid={thresholdIsInvalid} diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.test.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.test.tsx index c0bf73d6c5308..6aa829adc4544 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.test.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.test.tsx @@ -6,12 +6,11 @@ */ import React from 'react'; -import { shallowWithIntl } from '@kbn/test/jest'; import { fireEvent, waitFor } from '@testing-library/react'; import { FiltersExpressionsSelect } from './filters_expression_select'; import { render } from '../../../../lib/helper/rtl_helpers'; import { filterAriaLabels as aria } from './translations'; -import { filterLabels } from '../../filter_group/translations'; +import * as Hooks from '../../../../../../observability/public/hooks/use_values_list'; describe('FiltersExpressionSelect', () => { const LOCATION_FIELD_NAME = 'observer.geo.name'; @@ -19,23 +18,22 @@ describe('FiltersExpressionSelect', () => { const SCHEME_FIELD_NAME = 'monitor.type'; const TAG_FIELD_NAME = 'tags'; - it('is empty when no filters available', () => { - const component = shallowWithIntl( + it('is empty when no filters available', async () => { + const { queryByLabelText } = render( ); - expect(component).toMatchInlineSnapshot(``); + + await waitFor(() => { + for (const label of Object.values(aria)) { + expect(queryByLabelText(label)).toBeNull(); + } + }); }); it.each([ @@ -51,24 +49,20 @@ describe('FiltersExpressionSelect', () => { [aria.LOCATION, aria.TAG], ], [[TAG_FIELD_NAME], [aria.TAG], [aria.LOCATION, aria.PORT, aria.SCHEME]], - ])('contains provided new filter values', (newFilters, expectedLabels, absentLabels) => { + ])('contains provided new filter values', async (newFilters, expectedLabels, absentLabels) => { const { getByLabelText, queryByLabelText } = render( ); - expectedLabels.forEach((label) => expect(getByLabelText(label))); - absentLabels.forEach((label) => expect(queryByLabelText(label)).toBeNull()); + await waitFor(() => { + expectedLabels.forEach((label) => expect(getByLabelText(label))); + absentLabels.forEach((label) => expect(queryByLabelText(label)).toBeNull()); + }); }); it.each([ @@ -84,12 +78,6 @@ describe('FiltersExpressionSelect', () => { alertParams={{}} newFilters={[LOCATION_FIELD_NAME, SCHEME_FIELD_NAME, PORT_FIELD_NAME, TAG_FIELD_NAME]} onRemoveFilter={onRemoveFilterMock} - filters={{ - tags: ['prod'], - ports: [5601], - schemes: ['http'], - locations: ['nyc'], - }} setAlertParams={setAlertParamsMock} shouldUpdateUrl={false} /> @@ -108,60 +96,11 @@ describe('FiltersExpressionSelect', () => { }); }); - const TEST_TAGS = ['foo', 'bar']; - const TEST_PORTS = [5601, 9200]; - const TEST_SCHEMES = ['http', 'tcp']; - const TEST_LOCATIONS = ['nyc', 'fairbanks']; - it.each([ - [ - { - tags: TEST_TAGS, - ports: [5601, 9200], - schemes: ['http', 'tcp'], - locations: ['nyc', 'fairbanks'], - }, - [TAG_FIELD_NAME], - aria.TAG, - filterLabels.TAG, - TEST_TAGS, - ], - [ - { - tags: [], - ports: TEST_PORTS, - schemes: [], - locations: [], - }, - [PORT_FIELD_NAME], - aria.PORT, - filterLabels.PORT, - TEST_PORTS, - ], - [ - { - tags: [], - ports: [], - schemes: TEST_SCHEMES, - locations: [], - }, - [SCHEME_FIELD_NAME], - aria.SCHEME, - filterLabels.SCHEME, - TEST_SCHEMES, - ], - [ - { - tags: [], - ports: [], - schemes: [], - locations: TEST_LOCATIONS, - }, - [LOCATION_FIELD_NAME], - aria.LOCATION, - filterLabels.LOCATION, - TEST_LOCATIONS, - ], + [[TAG_FIELD_NAME], aria.TAG], + [[PORT_FIELD_NAME], aria.PORT], + [[SCHEME_FIELD_NAME], aria.SCHEME], + [[LOCATION_FIELD_NAME], aria.LOCATION], ])( 'applies accessible label to filter expressions, and contains selected filters', /** @@ -171,19 +110,14 @@ describe('FiltersExpressionSelect', () => { * @param filterLabel the name of the filter label expected in each item's aria-label * @param expectedFilterItems the set of filter options the component should render */ - async ( - filters, - newFilters, - expectedFilterButtonAriaLabel, - filterLabel, - expectedFilterItems - ) => { - const { getByLabelText } = render( + async (newFilters, expectedFilterButtonAriaLabel) => { + const spy = jest.spyOn(Hooks, 'useValuesList'); + spy.mockReturnValue({ loading: false, values: [{ label: 'test-label', count: 3 }] }); + const { getByLabelText, getByText } = render( @@ -194,9 +128,7 @@ describe('FiltersExpressionSelect', () => { fireEvent.click(filterButton); await waitFor(() => { - expectedFilterItems.forEach((filterItem: string | number) => - expect(getByLabelText(`Filter by ${filterLabel} ${filterItem}.`)) - ); + expect(getByText('Apply')); }); } ); diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx index b09d44488e803..cd0a78a42c5f7 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx @@ -7,31 +7,38 @@ import React, { useState } from 'react'; import { EuiButtonIcon, EuiExpression, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { FilterPopover } from '../../filter_group/filter_popover'; import { filterLabels } from '../../filter_group/translations'; import { alertFilterLabels, filterAriaLabels } from './translations'; -import { FilterExpressionsSelectProps } from './filters_expression_select_container'; -import { OverviewFiltersState } from '../../../../state/reducers/overview_filters'; +import { FieldValueSuggestions } from '../../../../../../observability/public'; +import { useIndexPattern } from '../../../../contexts/uptime_index_pattern_context'; +import { FILTER_FIELDS } from '../../../../../common/constants'; +import { useGetUrlParams } from '../../../../hooks'; -type Props = FilterExpressionsSelectProps & Pick; +export interface FilterExpressionsSelectProps { + alertParams: { [key: string]: any }; + newFilters: string[]; + onRemoveFilter: (val: string) => void; + setAlertParams: (key: string, value: any) => void; + shouldUpdateUrl: boolean; +} -export const FiltersExpressionsSelect: React.FC = ({ +const { TYPE, TAGS, LOCATION, PORT } = FILTER_FIELDS; + +export const FiltersExpressionsSelect: React.FC = ({ alertParams, - filters: overviewFilters, newFilters, onRemoveFilter, setAlertParams, }) => { - const { tags, ports, schemes, locations } = overviewFilters; - const alertFilters = alertParams?.filters; - const selectedPorts = alertFilters?.['url.port'] ?? []; - const selectedLocations = alertFilters?.['observer.geo.name'] ?? []; - const selectedSchemes = alertFilters?.['monitor.type'] ?? []; - const selectedTags = alertFilters?.tags ?? []; + const selectedPorts = alertFilters?.[PORT] ?? []; + const selectedLocations = alertFilters?.[LOCATION] ?? []; + const selectedSchemes = alertFilters?.[TYPE] ?? []; + const selectedTags = alertFilters?.[TAGS] ?? []; - const onFilterFieldChange = (fieldName: string, values: string[]) => { + const { dateRangeStart: from, dateRangeEnd: to } = useGetUrlParams(); + const onFilterFieldChange = (fieldName: string, values?: string[]) => { // the `filters` field is no longer a string if (alertParams.filters && typeof alertParams.filters !== 'string') { setAlertParams('filters', { ...alertParams.filters, [fieldName]: values }); @@ -41,12 +48,12 @@ export const FiltersExpressionsSelect: React.FC = ({ Object.assign( {}, { - tags: [], - 'url.port': [], - 'observer.geo.name': [], - 'monitor.type': [], + [TAGS]: [], + [PORT]: [], + [LOCATION]: [], + [TYPE]: [], }, - { [fieldName]: values } + { [fieldName]: values ?? [] } ) ); } @@ -54,13 +61,11 @@ export const FiltersExpressionsSelect: React.FC = ({ const monitorFilters = [ { - 'aria-label': filterAriaLabels.PORT, + ariaLabel: filterAriaLabels.PORT, onFilterFieldChange, loading: false, fieldName: 'url.port', id: 'filter_port', - disabled: ports?.length === 0, - items: ports?.map((p: number) => p.toString()) ?? [], selectedItems: selectedPorts, title: filterLabels.PORT, description: @@ -68,39 +73,33 @@ export const FiltersExpressionsSelect: React.FC = ({ value: selectedPorts.length === 0 ? alertFilterLabels.ANY_PORT : selectedPorts?.join(','), }, { - 'aria-label': filterAriaLabels.TAG, + ariaLabel: filterAriaLabels.TAG, onFilterFieldChange, loading: false, fieldName: 'tags', id: 'filter_tags', - disabled: tags?.length === 0, - items: tags ?? [], selectedItems: selectedTags, title: filterLabels.TAG, description: selectedTags.length === 0 ? alertFilterLabels.WITH : alertFilterLabels.WITH_TAG, value: selectedTags.length === 0 ? alertFilterLabels.ANY_TAG : selectedTags?.join(','), }, { - 'aria-label': filterAriaLabels.SCHEME, + ariaLabel: filterAriaLabels.SCHEME, onFilterFieldChange, loading: false, fieldName: 'monitor.type', id: 'filter_scheme', - disabled: schemes?.length === 0, - items: schemes ?? [], selectedItems: selectedSchemes, title: filterLabels.SCHEME, description: selectedSchemes.length === 0 ? alertFilterLabels.OF : alertFilterLabels.OF_TYPE, value: selectedSchemes.length === 0 ? alertFilterLabels.ANY_TYPE : selectedSchemes?.join(','), }, { - 'aria-label': filterAriaLabels.LOCATION, + ariaLabel: filterAriaLabels.LOCATION, onFilterFieldChange, loading: false, fieldName: 'observer.geo.name', id: 'filter_location', - disabled: locations?.length === 0, - items: locations ?? [], selectedItems: selectedLocations, title: filterLabels.LOCATION, description: @@ -123,43 +122,61 @@ export const FiltersExpressionsSelect: React.FC = ({ (curr) => curr.selectedItems.length > 0 || newFilters?.includes(curr.fieldName) ); + const indexPattern = useIndexPattern(); + return ( <> - {filtersToDisplay.map(({ description, value, ...item }) => ( - - - setIsOpen({ ...isOpen, [item.id]: !isOpen[item.id] })} + {filtersToDisplay.map( + ({ description, id, title, value, fieldName, ariaLabel, selectedItems }) => ( + + + {indexPattern && ( + { + onFilterFieldChange(fieldName, vals); + }} + selectedValue={selectedItems} + button={ + setIsOpen({ ...isOpen, [id]: !isOpen[id] })} + /> + } + forceOpen={isOpen[id]} + setForceOpen={() => { + setIsOpen({ ...isOpen, [id]: !isOpen[id] }); + }} + asCombobox={false} + cardinalityField="monitor.id" + time={{ from, to }} + allowExclusions={false} /> - } - forceOpen={isOpen[item.id]} - setForceOpen={() => { - setIsOpen({ ...isOpen, [item.id]: !isOpen[item.id] }); - }} - /> - - - { - onRemoveFilter(item.fieldName); - onFilterFieldChange(item.fieldName, []); - }} - /> - - - - ))} + )} + + + { + onRemoveFilter(fieldName); + onFilterFieldChange(fieldName, []); + }} + /> + + + + ) + )} ); }; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select_container.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select_container.tsx deleted file mode 100644 index 0c03d55ba38f5..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select_container.tsx +++ /dev/null @@ -1,25 +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 from 'react'; -import { useSelector } from 'react-redux'; -import { FiltersExpressionsSelect } from './filters_expression_select'; -import { overviewFiltersSelector } from '../../../../state/selectors'; - -export interface FilterExpressionsSelectProps { - alertParams: { [key: string]: any }; - newFilters: string[]; - onRemoveFilter: (val: string) => void; - setAlertParams: (key: string, value: any) => void; - shouldUpdateUrl: boolean; -} - -export const FiltersExpressionSelectContainer: React.FC = (props) => { - const overviewFilters = useSelector(overviewFiltersSelector); - - return ; -}; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/index.ts b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/index.ts index 85d0e82471e5c..6797517116ccd 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/index.ts +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/index.ts @@ -7,6 +7,5 @@ export { DownNoExpressionSelect } from './down_number_select'; export { FiltersExpressionsSelect } from './filters_expression_select'; -export { FiltersExpressionSelectContainer } from './filters_expression_select_container'; export { TimeExpressionSelect } from './time_expression_select'; export { StatusExpressionSelect } from './status_expression_select'; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.test.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.test.tsx index e161727b46b1b..b339410ef2409 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.test.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.test.tsx @@ -7,10 +7,45 @@ import React from 'react'; import { screen } from '@testing-library/dom'; -import { AlertMonitorStatusComponent, AlertMonitorStatusProps } from './alert_monitor_status'; +import { + AlertMonitorStatusComponent, + AlertMonitorStatusProps, + hasFilters, +} from './alert_monitor_status'; import { render } from '../../../../lib/helper/rtl_helpers'; describe('alert monitor status component', () => { + describe('hasFilters', () => { + const EMPTY_FILTERS = { + tags: [], + 'url.port': [], + 'observer.geo.name': [], + 'monitor.type': [], + }; + + it('returns false when filters are empty', () => { + expect(hasFilters({})).toBe(false); + }); + + it('returns false when all fields are empty', () => { + expect(hasFilters(EMPTY_FILTERS)).toBe(false); + }); + + it.each([ + { tags: ['prod'] }, + { 'url.port': ['5678'] }, + { 'observer.geo.name': ['Fairbanks'] }, + { 'monitor.type': ['HTTP'] }, + ])('returns true if a filter has a field', (testObj) => { + expect( + hasFilters({ + ...EMPTY_FILTERS, + ...testObj, + }) + ).toBe(true); + }); + }); + describe('AlertMonitorStatus', () => { const defaultProps: AlertMonitorStatusProps = { alertParams: { @@ -20,7 +55,6 @@ describe('alert monitor status component', () => { timerangeCount: 21, }, enabled: true, - hasFilters: false, isOldAlert: true, snapshotCount: 0, snapshotLoading: false, @@ -38,7 +72,7 @@ describe('alert monitor status component', () => { expect(await screen.findByText('Add filter')).toBeInTheDocument(); expect(await screen.findByText('Availability')).toBeInTheDocument(); expect(await screen.findByText('Status check')).toBeInTheDocument(); - expect(await screen.findByText('matching monitors are up in')).toBeInTheDocument(); + expect(await screen.findByText('any monitor is up in')).toBeInTheDocument(); expect(await screen.findByText('days')).toBeInTheDocument(); expect(await screen.findByText('hours')).toBeInTheDocument(); expect(await screen.findByText('within the last')).toBeInTheDocument(); diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx index eaae1650b02ed..8ed4b8f7a0032 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx @@ -8,17 +8,17 @@ import React, { useCallback, useEffect, useState } from 'react'; import { EuiCallOut, EuiSpacer, EuiHorizontalRule, EuiLoadingSpinner } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { FiltersExpressionSelectContainer, StatusExpressionSelect } from '../monitor_expressions'; +import { FiltersExpressionsSelect, StatusExpressionSelect } from '../monitor_expressions'; import { AddFilterButton } from './add_filter_btn'; import { OldAlertCallOut } from './old_alert_call_out'; import { AvailabilityExpressionSelect } from '../monitor_expressions/availability_expression_select'; import { AlertQueryBar } from '../alert_query_bar/query_bar'; import { useGetUrlParams } from '../../../../hooks'; +import { FILTER_FIELDS } from '../../../../../common/constants'; export interface AlertMonitorStatusProps { alertParams: { [key: string]: any }; enabled: boolean; - hasFilters: boolean; isOldAlert: boolean; snapshotCount: number; snapshotLoading?: boolean; @@ -30,15 +30,16 @@ export interface AlertMonitorStatusProps { }; } +export const hasFilters = (filters?: { [key: string]: string[] }) => { + if (!filters || Object.keys(filters).length === 0) { + return false; + } + + return Object.values(FILTER_FIELDS).some((f) => filters[f].length); +}; + export const AlertMonitorStatusComponent: React.FC = (props) => { - const { - alertParams, - hasFilters, - isOldAlert, - setAlertParams, - snapshotCount, - snapshotLoading, - } = props; + const { alertParams, isOldAlert, setAlertParams, snapshotCount, snapshotLoading } = props; const alertFilters = alertParams?.filters ?? {}; const [newFilters, setNewFilters] = useState( @@ -94,7 +95,7 @@ export const AlertMonitorStatusComponent: React.FC = (p }} /> - { @@ -110,8 +111,8 @@ export const AlertMonitorStatusComponent: React.FC = (p @@ -120,6 +121,7 @@ export const AlertMonitorStatusComponent: React.FC = (p alertParams={alertParams} isOldAlert={isOldAlert} setAlertParams={setAlertParams} + hasFilters={hasFilters(alertParams?.filters)} /> diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/translations.ts b/x-pack/plugins/uptime/public/components/overview/alerts/translations.ts index 7cfcdabe5562b..76d8ebaea3719 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/translations.ts +++ b/x-pack/plugins/uptime/public/components/overview/alerts/translations.ts @@ -197,6 +197,15 @@ export const ENTER_AVAILABILITY_THRESHOLD_DESCRIPTION = i18n.translate( } ); +export const ENTER_ANY_AVAILABILITY_THRESHOLD_DESCRIPTION = i18n.translate( + 'xpack.uptime.alerts.monitorStatus.availability.threshold.anyMonitorDescription', + { + defaultMessage: 'any monitor is up in', + description: + 'This fragment explains that an alert will fire for monitors matching user-specified criteria', + } +); + export const ENTER_AVAILABILITY_THRESHOLD_VALUE = (value: string) => i18n.translate('xpack.uptime.alerts.monitorStatus.availability.threshold.value', { defaultMessage: '< {value}% of checks', diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/__snapshots__/parse_filter_map.test.ts.snap b/x-pack/plugins/uptime/public/components/overview/filter_group/__snapshots__/parse_filter_map.test.ts.snap deleted file mode 100644 index cabad2818fa90..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/__snapshots__/parse_filter_map.test.ts.snap +++ /dev/null @@ -1,20 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`parseFiltersMap provides values from valid filter string 1`] = ` -Object { - "locations": Array [ - "us-east-2", - ], - "ports": Array [ - "5601", - "80", - ], - "schemes": Array [ - "http", - "tcp", - ], - "tags": Array [], -} -`; - -exports[`parseFiltersMap returns an empty object for invalid filter 1`] = `"Unable to parse invalid filter string"`; diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.test.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.test.tsx index 807f95c22bc61..cdc399a750756 100644 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.test.tsx +++ b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.test.tsx @@ -8,67 +8,103 @@ import React from 'react'; import { fireEvent, waitFor } from '@testing-library/react'; import { render } from '../../../lib/helper/rtl_helpers'; -import { FilterGroupComponent } from './filter_group'; +import { FilterGroup } from './filter_group'; +import * as Hooks from '../../../../../observability/public/hooks/use_values_list'; -describe('FilterGroupComponent', () => { - const overviewFilters = { - locations: ['nyc', 'fairbanks'], - ports: [5601, 9200], - schemes: ['http', 'tcp'], - tags: ['prod', 'dev'], - }; +describe('FilterGroup', () => { it.each([ - ['expands filter group for Location filter', 'Search for location'], - ['expands filter group for Port filter', 'Search for port'], - ['expands filter group for Scheme filter', 'Search for scheme'], - ['expands filter group for Tag filter', 'Search for tag'], - ])('handles loading', async (popoverButtonLabel, searchInputLabel) => { - const { getByLabelText } = render( - - ); + ['expands filter group for Location filter'], + ['expands filter group for Port filter'], + ['expands filter group for Scheme filter'], + ['expands filter group for Tag filter'], + ])('handles loading', async (popoverButtonLabel) => { + jest.spyOn(Hooks, 'useValuesList').mockReturnValue({ + values: [], + loading: true, + }); + const { getByLabelText, getByText } = render(); - const popoverButton = getByLabelText(popoverButtonLabel); - fireEvent.click(popoverButton); await waitFor(() => { - const searchInput = getByLabelText(searchInputLabel); - expect(searchInput).toHaveAttribute('placeholder', 'Loading...'); + const popoverButton = getByLabelText(popoverButtonLabel); + fireEvent.click(popoverButton); + }); + await waitFor(() => { + expect(getByText('Loading options')); }); }); it.each([ [ 'expands filter group for Location filter', - 'Search for location', - ['Filter by Location nyc.', 'Filter by Location fairbanks.'], + [ + [ + { + label: 'Fairbanks', + count: 10, + }, + { + label: 'NYC', + count: 2, + }, + ], + [], + [], + [], + ], ], [ 'expands filter group for Port filter', - 'Search for port', - ['Filter by Port 5601.', 'Filter by Port 9200.'], + [ + [], + [ + { label: '80', count: 12 }, + { label: '443', count: 8 }, + ], + [], + [], + ], ], [ 'expands filter group for Scheme filter', - 'Search for scheme', - ['Filter by Scheme http.', 'Filter by Scheme tcp.'], + [ + [], + [], + [ + { label: 'HTTP', count: 15 }, + { label: 'TCP', count: 10 }, + ], + [], + ], ], [ 'expands filter group for Tag filter', - 'Search for tag', - ['Filter by Tag prod.', 'Filter by Tag dev.'], + [ + [], + [], + [], + [ + { label: 'test', count: 23 }, + { label: 'prod', count: 10 }, + ], + ], ], - ])( - 'displays filter items when clicked', - async (popoverButtonLabel, searchInputLabel, filterItemButtonLabels) => { - const { getByLabelText } = render( - - ); + ])('displays filter item counts when clicked', async (popoverButtonLabel, values) => { + const spy = jest.spyOn(Hooks, 'useValuesList'); + for (let i = 0; i < 4; i++) { + spy.mockReturnValueOnce({ + values: values[i], + loading: false, + }); + } + const { getByLabelText, getAllByLabelText } = render(); + + await waitFor(() => { const popoverButton = getByLabelText(popoverButtonLabel); fireEvent.click(popoverButton); - await waitFor(() => { - expect(getByLabelText(searchInputLabel)); - filterItemButtonLabels.forEach((itemLabel) => expect(getByLabelText(itemLabel))); - }); - } - ); + }); + + expect(getByLabelText('2 available filters')); + expect(getAllByLabelText('0 available filters')).toHaveLength(3); + }); }); diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx index b60d2b3050f5c..3980b4bf9d3da 100644 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx +++ b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx @@ -5,100 +5,72 @@ * 2.0. */ -import React, { useState } from 'react'; +import React, { useCallback, useState } from 'react'; import { EuiFilterGroup } from '@elastic/eui'; import styled from 'styled-components'; -import { useRouteMatch } from 'react-router-dom'; -import { FilterPopoverProps, FilterPopover } from './filter_popover'; -import { OverviewFilters } from '../../../../common/runtime_types/overview_filters'; -import { filterLabels } from './translations'; import { useFilterUpdate } from '../../../hooks/use_filter_update'; -import { MONITOR_ROUTE } from '../../../../common/constants'; import { useSelectedFilters } from '../../../hooks/use_selected_filters'; - -interface Props { - loading: boolean; - overviewFilters: OverviewFilters; -} +import { FieldValueSuggestions } from '../../../../../observability/public'; +import { SelectedFilters } from './selected_filters'; +import { useIndexPattern } from '../../../contexts/uptime_index_pattern_context'; +import { useGetUrlParams } from '../../../hooks'; const Container = styled(EuiFilterGroup)` margin-bottom: 10px; `; -function isDisabled(array?: T[]) { - return array ? array.length === 0 : true; -} - -export const FilterGroupComponent: React.FC = ({ overviewFilters, loading }) => { - const { locations, ports, schemes, tags } = overviewFilters; - +export const FilterGroup = () => { const [updatedFieldValues, setUpdatedFieldValues] = useState<{ fieldName: string; values: string[]; - }>({ fieldName: '', values: [] }); + notValues: string[]; + }>({ fieldName: '', values: [], notValues: [] }); - useFilterUpdate(updatedFieldValues.fieldName, updatedFieldValues.values); + useFilterUpdate( + updatedFieldValues.fieldName, + updatedFieldValues.values, + updatedFieldValues.notValues + ); - const { selectedLocations, selectedPorts, selectedSchemes, selectedTags } = useSelectedFilters(); + const { dateRangeStart, dateRangeEnd } = useGetUrlParams(); - const onFilterFieldChange = (fieldName: string, values: string[]) => { - setUpdatedFieldValues({ fieldName, values }); - }; + const { filtersList } = useSelectedFilters(); - const isMonitorPage = useRouteMatch(MONITOR_ROUTE); + const indexPattern = useIndexPattern(); - const filterPopoverProps: FilterPopoverProps[] = [ - { - loading, - onFilterFieldChange, - fieldName: 'observer.geo.name', - id: 'location', - items: locations || [], - selectedItems: selectedLocations, - title: filterLabels.LOCATION, + const onFilterFieldChange = useCallback( + (fieldName: string, values: string[], notValues: string[]) => { + setUpdatedFieldValues({ fieldName, values, notValues }); }, - // on monitor page we only display location filter in ping list - ...(!isMonitorPage - ? [ - { - loading, - onFilterFieldChange, - fieldName: 'url.port', - id: 'port', - disabled: isDisabled(ports), - items: ports?.map((p: number) => p.toString()) ?? [], - selectedItems: selectedPorts, - title: filterLabels.PORT, - }, - { - loading, - onFilterFieldChange, - fieldName: 'monitor.type', - id: 'scheme', - disabled: isDisabled(schemes), - items: schemes ?? [], - selectedItems: selectedSchemes, - title: filterLabels.SCHEME, - }, - { - loading, - onFilterFieldChange, - fieldName: 'tags', - id: 'tags', - disabled: isDisabled(tags), - items: tags ?? [], - selectedItems: selectedTags, - title: filterLabels.TAG, - }, - ] - : []), - ]; + [] + ); return ( - - {filterPopoverProps.map((item) => ( - - ))} - + <> + + {indexPattern && + filtersList.map(({ field, label, selectedItems, excludedItems }) => ( + + onFilterFieldChange(field, values ?? [], notValues ?? []) + } + asCombobox={false} + asFilterButton={true} + forceOpen={false} + filters={[]} + cardinalityField="monitor.id" + time={{ from: dateRangeStart, to: dateRangeEnd }} + /> + ))} + + + ); }; diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group_container.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group_container.tsx deleted file mode 100644 index db1892526a1e6..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group_container.tsx +++ /dev/null @@ -1,57 +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, { useContext, useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { useGetUrlParams } from '../../../hooks'; -import { parseFiltersMap } from './parse_filter_map'; -import { fetchOverviewFilters } from '../../../state/actions'; -import { FilterGroupComponent } from './index'; -import { UptimeRefreshContext } from '../../../contexts'; -import { esKuerySelector, overviewFiltersSelector } from '../../../state/selectors'; - -interface Props { - esFilters?: string; -} - -export const FilterGroup: React.FC = ({ esFilters }: Props) => { - const { lastRefresh } = useContext(UptimeRefreshContext); - - const { filters: overviewFilters, loading } = useSelector(overviewFiltersSelector); - const esKuery = useSelector(esKuerySelector); - - const { dateRangeStart, dateRangeEnd, statusFilter, filters: urlFilters } = useGetUrlParams(); - - const dispatch = useDispatch(); - - useEffect(() => { - const filterSelections = parseFiltersMap(urlFilters); - dispatch( - fetchOverviewFilters({ - dateRangeStart, - dateRangeEnd, - locations: filterSelections.locations ?? [], - ports: filterSelections.ports ?? [], - schemes: filterSelections.schemes ?? [], - search: esKuery, - statusFilter, - tags: filterSelections.tags ?? [], - }) - ); - }, [ - lastRefresh, - dateRangeStart, - dateRangeEnd, - esKuery, - esFilters, - statusFilter, - urlFilters, - dispatch, - ]); - - return ; -}; diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.test.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.test.tsx deleted file mode 100644 index bccebb21718bf..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.test.tsx +++ /dev/null @@ -1,96 +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 from 'react'; -import { fireEvent, waitFor, waitForElementToBeRemoved } from '@testing-library/react'; -import { FilterPopoverProps, FilterPopover } from './filter_popover'; -import { render } from '../../../lib/helper/rtl_helpers'; - -describe('FilterPopover component', () => { - let props: FilterPopoverProps; - - beforeEach(() => { - props = { - fieldName: 'test-fieldName', - id: 'test', - loading: false, - items: ['first', 'second', 'third', 'fourth'], - onFilterFieldChange: jest.fn(), - selectedItems: ['first', 'third'], - title: 'test-title', - }; - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('expands on button click', () => { - const { getByRole, getByLabelText, getByText, queryByLabelText, queryByText } = render( - - ); - - const screenReaderOnlyText = 'You are in a dialog. To close this dialog, hit escape.'; - - expect(queryByText(screenReaderOnlyText)).toBeNull(); - expect(queryByLabelText('Filter by bar fourth.')).toBeNull(); - - fireEvent.click(getByRole('button')); - - expect(getByText(screenReaderOnlyText)); - expect(getByLabelText('Filter by test-title fourth.')); - }); - - it('does not show item list when loading, and displays placeholder', async () => { - props.loading = true; - const { getByRole, queryByText, getByLabelText } = render(); - - fireEvent.click(getByRole('button')); - - await waitFor(() => { - const search = getByLabelText('Search for test-title'); - expect(search).toHaveAttribute('placeholder', 'Loading...'); - }); - - expect(queryByText('Filter by test-title second.')).toBeNull(); - }); - - it.each([ - [[], ['third'], ['third']], - [['first', 'third'], ['first'], ['third']], - [['fourth'], ['first', 'second'], ['first', 'second', 'fourth']], - ])( - 'returns selected items on popover close', - async (selectedPropsItems, expectedSelections, itemsToClick) => { - if (itemsToClick.length < 1) { - throw new Error('This test assumes at least one item will be clicked'); - } - props.selectedItems = selectedPropsItems; - - const { getByLabelText, queryByLabelText } = render(); - - const uptimeFilterButton = getByLabelText(`expands filter group for ${props.title} filter`); - - fireEvent.click(uptimeFilterButton); - - const generateLabelText = (item: string) => `Filter by ${props.title} ${item}.`; - - itemsToClick.forEach((item) => { - const optionButtonLabelText = generateLabelText(item); - const optionButton = getByLabelText(optionButtonLabelText); - fireEvent.click(optionButton); - }); - - fireEvent.click(uptimeFilterButton); - - await waitForElementToBeRemoved(() => queryByLabelText(generateLabelText(itemsToClick[0]))); - - expect(props.onFilterFieldChange).toHaveBeenCalledTimes(1); - expect(props.onFilterFieldChange).toHaveBeenCalledWith(props.fieldName, expectedSelections); - } - ); -}); diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.tsx deleted file mode 100644 index 23e17802a6835..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.tsx +++ /dev/null @@ -1,146 +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 { EuiFieldSearch, EuiFilterSelectItem, EuiPopover, EuiPopoverTitle } from '@elastic/eui'; -import React, { useState, useEffect } from 'react'; -import { i18n } from '@kbn/i18n'; -import { UptimeFilterButton } from './uptime_filter_button'; -import { toggleSelectedItems } from './toggle_selected_item'; -import { LocationLink } from '../monitor_list'; - -export interface FilterPopoverProps { - fieldName: string; - id: string; - loading: boolean; - disabled?: boolean; - items: string[]; - onFilterFieldChange: (fieldName: string, values: string[]) => void; - selectedItems: string[]; - title: string; - btnContent?: JSX.Element; - forceOpen?: boolean; - setForceOpen?: (val: boolean) => void; -} - -const isItemSelected = (selectedItems: string[], item: string): 'on' | undefined => - selectedItems.find((selected) => selected === item) ? 'on' : undefined; - -export const FilterPopover = ({ - fieldName, - id, - disabled, - loading, - items: allItems, - onFilterFieldChange, - selectedItems, - title, - btnContent, - forceOpen, - setForceOpen, -}: FilterPopoverProps) => { - const [isOpen, setIsOpen] = useState(false); - const [itemsToDisplay, setItemsToDisplay] = useState([]); - const [searchQuery, setSearchQuery] = useState(''); - const [tempSelectedItems, setTempSelectedItems] = useState(selectedItems); - - const [items, setItems] = useState([]); - - useEffect(() => { - // Merge incoming items with selected items, to enable deselection - - const mItems = selectedItems.concat(allItems ?? []); - const newItems = mItems.filter((item, index) => mItems.indexOf(item) === index); - setItems(newItems); - setTempSelectedItems(selectedItems); - }, [allItems, selectedItems]); - - useEffect(() => { - if (searchQuery !== '') { - const toDisplay = items.filter((item) => item.indexOf(searchQuery) >= 0); - setItemsToDisplay(toDisplay); - } else { - setItemsToDisplay(items); - } - }, [searchQuery, items]); - - return ( - 0} - numFilters={items.length} - numActiveFilters={isOpen ? tempSelectedItems.length : selectedItems.length} - onClick={() => { - if (isOpen) { - // only update these values on close - onFilterFieldChange(fieldName, tempSelectedItems); - } - setIsOpen(!isOpen); - }} - title={title} - /> - ) - } - closePopover={() => { - setIsOpen(false); - onFilterFieldChange(fieldName, tempSelectedItems); - if (setForceOpen) { - setForceOpen(false); - } - }} - data-test-subj={`filter-popover_${id}`} - id={id} - isOpen={isOpen || forceOpen} - ownFocus={true} - zIndex={10000} - > - - setSearchQuery(query)} - aria-label={i18n.translate('xpack.uptime.filterPopout.searchMessage.ariaLabel', { - defaultMessage: 'Search for {title}', - values: { - title: title.toLowerCase(), - }, - })} - placeholder={ - loading - ? i18n.translate('xpack.uptime.filterPopout.loadingMessage', { - defaultMessage: 'Loading...', - }) - : i18n.translate('xpack.uptime.filterPopout.searchMessage', { - defaultMessage: 'Search {title}', - values: { - title: title.toLowerCase(), - }, - }) - } - /> - - {!loading && - itemsToDisplay.map((item) => ( - toggleSelectedItems(item, tempSelectedItems, setTempSelectedItems)} - > - {item} - - ))} - {id === 'location' && items.length === 0 && } - - ); -}; diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/index.ts b/x-pack/plugins/uptime/public/components/overview/filter_group/index.ts deleted file mode 100644 index befe65560dfd6..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/index.ts +++ /dev/null @@ -1,9 +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. - */ - -export { FilterGroupComponent } from './filter_group'; -export { FilterGroup } from './filter_group_container'; diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/parse_filter_map.test.ts b/x-pack/plugins/uptime/public/components/overview/filter_group/parse_filter_map.test.ts deleted file mode 100644 index d06af65cee73d..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/parse_filter_map.test.ts +++ /dev/null @@ -1,22 +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 { parseFiltersMap } from './parse_filter_map'; - -describe('parseFiltersMap', () => { - it('provides values from valid filter string', () => { - expect( - parseFiltersMap( - '[["url.port",["5601","80"]],["observer.geo.name",["us-east-2"]],["monitor.type",["http","tcp"]]]' - ) - ).toMatchSnapshot(); - }); - - it('returns an empty object for invalid filter', () => { - expect(() => parseFiltersMap('some invalid string')).toThrowErrorMatchingSnapshot(); - }); -}); diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/parse_filter_map.ts b/x-pack/plugins/uptime/public/components/overview/filter_group/parse_filter_map.ts deleted file mode 100644 index 5d08847b6b713..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/parse_filter_map.ts +++ /dev/null @@ -1,39 +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. - */ - -interface FilterField { - name: string; - fieldName: string; -} - -/** - * These are the only filter fields we are looking to catch at the moment. - * If your code needs to support custom fields, introduce a second parameter to - * `parseFiltersMap` to take a list of FilterField objects. - */ -const filterAllowList: FilterField[] = [ - { name: 'ports', fieldName: 'url.port' }, - { name: 'locations', fieldName: 'observer.geo.name' }, - { name: 'tags', fieldName: 'tags' }, - { name: 'schemes', fieldName: 'monitor.type' }, -]; - -export const parseFiltersMap = (filterMapString: string) => { - if (!filterMapString) { - return {}; - } - const filterSlices: { [key: string]: any } = {}; - try { - const map = new Map(JSON.parse(filterMapString)); - filterAllowList.forEach(({ name, fieldName }) => { - filterSlices[name] = map.get(fieldName) ?? []; - }); - return filterSlices; - } catch { - throw new Error('Unable to parse invalid filter string'); - } -}; diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/selected_filters.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/selected_filters.tsx new file mode 100644 index 0000000000000..7e70673016d2c --- /dev/null +++ b/x-pack/plugins/uptime/public/components/overview/filter_group/selected_filters.tsx @@ -0,0 +1,79 @@ +/* + * 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 { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { FilterValueLabel } from '../../../../../observability/public'; +import { useIndexPattern } from '../../../contexts/uptime_index_pattern_context'; +import { useSelectedFilters } from '../../../hooks/use_selected_filters'; + +interface Props { + onChange: (fieldName: string, values: string[], notValues: string[]) => void; +} +export const SelectedFilters = ({ onChange }: Props) => { + const indexPattern = useIndexPattern(); + const { filtersList } = useSelectedFilters(); + + if (!indexPattern) return null; + + return ( + + {filtersList.map(({ field, selectedItems, excludedItems, label }) => [ + ...selectedItems.map((value) => ( + + { + onChange( + field, + selectedItems.filter((valT) => valT !== value), + excludedItems + ); + }} + invertFilter={(val) => { + onChange( + field, + selectedItems.filter((valT) => valT !== value), + [...excludedItems, value] + ); + }} + field={field} + value={value} + negate={false} + label={label} + /> + + )), + ...excludedItems.map((value) => ( + + { + onChange( + field, + selectedItems, + excludedItems.filter((valT) => valT !== value) + ); + }} + invertFilter={(val) => { + onChange( + field, + [...selectedItems, value], + excludedItems.filter((valT) => valT !== value) + ); + }} + field={field} + value={value} + negate={true} + label={label} + /> + + )), + ])} + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/toggle_selected_item.test.ts b/x-pack/plugins/uptime/public/components/overview/filter_group/toggle_selected_item.test.ts deleted file mode 100644 index 0a80f2062320d..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/toggle_selected_item.test.ts +++ /dev/null @@ -1,24 +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 { toggleSelectedItems } from './toggle_selected_item'; - -describe('toggleSelectedItems', () => { - it(`adds the item if it's not in the list`, () => { - const mock = jest.fn(); - toggleSelectedItems('abc', ['aba', 'abd'], mock); - expect(mock).toHaveBeenCalledTimes(1); - expect(mock).toHaveBeenCalledWith(['aba', 'abd', 'abc']); - }); - - it(`removes the item if it's already in the list`, () => { - const mock = jest.fn(); - toggleSelectedItems('abc', ['aba', 'abc', 'abd'], mock); - expect(mock).toHaveBeenCalledTimes(1); - expect(mock).toHaveBeenCalledWith(['aba', 'abd']); - }); -}); diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/toggle_selected_item.ts b/x-pack/plugins/uptime/public/components/overview/filter_group/toggle_selected_item.ts deleted file mode 100644 index 08b031f936dc5..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/toggle_selected_item.ts +++ /dev/null @@ -1,23 +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 { Dispatch, SetStateAction } from 'react'; - -export const toggleSelectedItems = ( - item: string, - tempSelectedItems: string[], - setTempSelectedItems: Dispatch> -) => { - const index = tempSelectedItems.indexOf(item); - const nextSelectedItems = [...tempSelectedItems]; - if (index >= 0) { - nextSelectedItems.splice(index, 1); - } else { - nextSelectedItems.push(item); - } - setTempSelectedItems(nextSelectedItems); -}; diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/uptime_filter_button.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/uptime_filter_button.tsx deleted file mode 100644 index 326ad7a292455..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/uptime_filter_button.tsx +++ /dev/null @@ -1,44 +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 { EuiFilterButton } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; - -interface UptimeFilterButtonProps { - isDisabled?: boolean; - isSelected: boolean; - numFilters: number; - numActiveFilters: number; - onClick: () => void; - title: string; -} - -export const UptimeFilterButton = ({ - isDisabled, - isSelected, - numFilters, - numActiveFilters, - onClick, - title, -}: UptimeFilterButtonProps) => ( - - {title} - -); diff --git a/x-pack/plugins/uptime/public/components/overview/index.ts b/x-pack/plugins/uptime/public/components/overview/index.ts index 729db44aaa964..d647c38cee1ca 100644 --- a/x-pack/plugins/uptime/public/components/overview/index.ts +++ b/x-pack/plugins/uptime/public/components/overview/index.ts @@ -7,6 +7,5 @@ export * from './monitor_list'; export * from './empty_state'; -export * from './filter_group'; export * from './alerts'; export * from './snapshot'; diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_name_col.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_name_col.tsx index 7adf248d37fc5..65bbaf45b63a2 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_name_col.tsx +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_name_col.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState } from 'react'; +import React, { useState, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonEmpty, EuiText } from '@elastic/eui'; import { MonitorPageLink } from '../../../common/monitor_page_link'; @@ -44,7 +44,12 @@ export const MonitorNameColumn = ({ summary }: Props) => { const [filterType, setFilterType] = useState(currFilters.get('monitor.type') ?? []); - useFilterUpdate('monitor.type', filterType); + const excludedTypeFilters = useMemo(() => { + const currExcludedFilters = parseCurrentFilters(params.excludedFilters); + return currExcludedFilters.get('monitor.type') ?? []; + }, [params.excludedFilters]); + + useFilterUpdate('monitor.type', filterType, excludedTypeFilters); const filterLabel = i18n.translate('xpack.uptime.monitorList.monitorType.filter', { defaultMessage: 'Filter all monitors with type {type}', diff --git a/x-pack/plugins/uptime/public/components/overview/query_bar/query_bar.tsx b/x-pack/plugins/uptime/public/components/overview/query_bar/query_bar.tsx index 9436f420f7740..3c8c0599efa69 100644 --- a/x-pack/plugins/uptime/public/components/overview/query_bar/query_bar.tsx +++ b/x-pack/plugins/uptime/public/components/overview/query_bar/query_bar.tsx @@ -9,10 +9,9 @@ import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexItem } from '@elastic/eui'; import { QueryStringInput } from '../../../../../../../src/plugins/data/public/'; -import { useIndexPattern } from './use_index_pattern'; import { SyntaxType, useQueryBar } from './use_query_bar'; import { KQL_PLACE_HOLDER, SIMPLE_SEARCH_PLACEHOLDER } from './translations'; -import { useGetUrlParams } from '../../../hooks'; +import { useGetUrlParams, useIndexPattern } from '../../../hooks'; const SYNTAX_STORAGE = 'uptime:queryBarSyntax'; @@ -36,7 +35,7 @@ export const QueryBar = () => { const { query, setQuery, submitImmediately } = useQueryBar(); - const { index_pattern: indexPattern } = useIndexPattern(); + const indexPattern = useIndexPattern(); const [inputVal, setInputVal] = useState(query.query as string); diff --git a/x-pack/plugins/uptime/public/components/overview/query_bar/use_index_pattern.ts b/x-pack/plugins/uptime/public/components/overview/query_bar/use_index_pattern.ts deleted file mode 100644 index b0e567c40ed73..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/query_bar/use_index_pattern.ts +++ /dev/null @@ -1,25 +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 } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { getIndexPattern } from '../../../state/actions'; -import { selectIndexPattern } from '../../../state/selectors'; - -export const useIndexPattern = () => { - const dispatch = useDispatch(); - const indexPattern = useSelector(selectIndexPattern); - - useEffect(() => { - // we only use index pattern for kql queries - if (!indexPattern.index_pattern) { - dispatch(getIndexPattern()); - } - }, [indexPattern.index_pattern, dispatch]); - - return indexPattern; -}; diff --git a/x-pack/plugins/uptime/public/components/overview/query_bar/use_query_bar.test.tsx b/x-pack/plugins/uptime/public/components/overview/query_bar/use_query_bar.test.tsx new file mode 100644 index 0000000000000..e4c57dab0ffcf --- /dev/null +++ b/x-pack/plugins/uptime/public/components/overview/query_bar/use_query_bar.test.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 from 'react'; +import { waitFor } from '@testing-library/react'; +import { act, renderHook } from '@testing-library/react-hooks'; +import { MockRouter, MockKibanaProvider } from '../../../lib/helper/rtl_helpers'; +import { SyntaxType, useQueryBar, DEBOUNCE_INTERVAL } from './use_query_bar'; +import { MountWithReduxProvider } from '../../../lib'; +import * as URL from '../../../hooks/use_url_params'; +import * as ES_FILTERS from '../../../hooks/update_kuery_string'; +import { UptimeUrlParams } from '../../../lib/helper/url_params'; + +const SAMPLE_ES_FILTERS = `{"bool":{"should":[{"match_phrase":{"monitor.id":"NodeServer"}}],"minimum_should_match":1}}`; + +describe('useQueryBar', () => { + let DEFAULT_URL_PARAMS: UptimeUrlParams; + let wrapper: any; + let useUrlParamsSpy: jest.SpyInstance<[URL.GetUrlParams, URL.UpdateUrlParams]>; + let useGetUrlParamsSpy: jest.SpyInstance; + let updateUrlParamsMock: jest.Mock; + let useUpdateKueryStringSpy: jest.SpyInstance; + + beforeEach(() => { + DEFAULT_URL_PARAMS = { + absoluteDateRangeStart: 100, + absoluteDateRangeEnd: 200, + autorefreshInterval: 10000, + autorefreshIsPaused: true, + dateRangeStart: 'now-15m', + dateRangeEnd: 'now', + excludedFilters: '', + filters: '', + query: '', + search: 'monitor.id: "My-Monitor"', + statusFilter: '', + }; + wrapper = ({ children }: any) => ( + + + {children} + + + ); + useUrlParamsSpy = jest.spyOn(URL, 'useUrlParams'); + useGetUrlParamsSpy = jest.spyOn(URL, 'useGetUrlParams'); + useUpdateKueryStringSpy = jest.spyOn(ES_FILTERS, 'useUpdateKueryString'); + updateUrlParamsMock = jest.fn(); + + useUrlParamsSpy.mockImplementation(() => [jest.fn(), updateUrlParamsMock]); + useGetUrlParamsSpy.mockReturnValue(DEFAULT_URL_PARAMS); + useUpdateKueryStringSpy.mockReturnValue([SAMPLE_ES_FILTERS]); + }); + + it.each([ + [SyntaxType.text, undefined, SAMPLE_ES_FILTERS, '', 'monitor.id: "My-Other-Monitor"', false, 0], + [ + SyntaxType.kuery, + new Error('there was a problem'), + SAMPLE_ES_FILTERS, + '', + 'monitor.id: "My-Other-Monitor"', + false, + 0, + ], + [SyntaxType.kuery, undefined, undefined, '', 'monitor.id: "My-Other-Monitor"', false, 0], + [SyntaxType.text, undefined, undefined, '', 'monitor.id: "My-Other-Monitor"', false, 0], + [SyntaxType.text, undefined, undefined, 'my-search', 'monitor.id: "My-Other-Monitor"', true, 1], + [SyntaxType.kuery, undefined, undefined, 'my-search', '', true, 1], + [ + SyntaxType.kuery, + undefined, + SAMPLE_ES_FILTERS, + 'my-search', + 'monitor.id: "My-Monitor"', + true, + 1, + ], + ])( + 'updates URL only when conditions are appropriate', + /** + * This test is designed to prevent massive duplication of boilerplate; each set of parameters should trigger + * a different response from the hook. At the end, we wait for the debounce interval to elapse and then check + * whether the URL was updated. + * + * @param language the query syntax + * @param error an error resulting from parsing es filters + * @param esFilters the AST string generated from parsing kuery syntax + * @param search the simple text search + * @param query the new kuery entered by the user + * @param shouldExpectCall boolean denoting whether or not the test should expect the url to be updated + * @param calledTimes the number of times the test should expect the url to be updated + */ + async (language, error, esFilters, search, query, shouldExpectCall, calledTimes) => { + const { + result: { current }, + } = renderHook(() => useQueryBar(), { wrapper }); + + useUpdateKueryStringSpy.mockReturnValue([esFilters, error]); + useGetUrlParamsSpy.mockReturnValue({ + ...DEFAULT_URL_PARAMS, + search, + }); + + act(() => { + current.setQuery({ + query, + language, + }); + }); + + await waitFor(async () => { + await new Promise((r) => setInterval(r, DEBOUNCE_INTERVAL + 50)); + if (shouldExpectCall) { + expect(updateUrlParamsMock).toHaveBeenCalledTimes(calledTimes); + } else { + expect(updateUrlParamsMock).not.toHaveBeenCalled(); + } + }); + } + ); +}); diff --git a/x-pack/plugins/uptime/public/components/overview/query_bar/use_query_bar.ts b/x-pack/plugins/uptime/public/components/overview/query_bar/use_query_bar.ts index 2f2d8bf092ddf..4dd431e9617a3 100644 --- a/x-pack/plugins/uptime/public/components/overview/query_bar/use_query_bar.ts +++ b/x-pack/plugins/uptime/public/components/overview/query_bar/use_query_bar.ts @@ -9,9 +9,13 @@ import React, { useCallback, useState } from 'react'; import useDebounce from 'react-use/lib/useDebounce'; import { useDispatch } from 'react-redux'; import { Query } from 'src/plugins/data/common'; -import { useGetUrlParams, useUpdateKueryString, useUrlParams } from '../../../hooks'; +import { + useGetUrlParams, + useIndexPattern, + useUpdateKueryString, + useUrlParams, +} from '../../../hooks'; import { setEsKueryString } from '../../../state/actions'; -import { useIndexPattern } from './use_index_pattern'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { UptimePluginServices } from '../../../apps/plugin'; @@ -35,6 +39,8 @@ interface UseQueryBarUtils { submitImmediately: () => void; } +export const DEBOUNCE_INTERVAL = 250; + /** * Provides state management and automatic dispatching of a Query object. * @@ -44,7 +50,7 @@ export const useQueryBar = (): UseQueryBarUtils => { const dispatch = useDispatch(); const { absoluteDateRangeStart, absoluteDateRangeEnd, ...params } = useGetUrlParams(); - const { search, query: queryParam, filters: paramFilters } = params; + const { search, query: queryParam, filters: paramFilters, excludedFilters } = params; const { services: { storage }, @@ -64,14 +70,15 @@ export const useQueryBar = (): UseQueryBarUtils => { } ); - const { index_pattern: indexPattern } = useIndexPattern(); + const indexPattern = useIndexPattern(); - const updateUrlParams = useUrlParams()[1]; + const [, updateUrlParams] = useUrlParams(); const [esFilters, error] = useUpdateKueryString( indexPattern, query.language === SyntaxType.kuery ? (query.query as string) : undefined, - paramFilters + paramFilters, + excludedFilters ); const setEsKueryFilters = useCallback( @@ -92,7 +99,7 @@ export const useQueryBar = (): UseQueryBarUtils => { if (query.language === SyntaxType.text && queryParam !== query.query) { updateUrlParams({ query: query.query as string }); } - if (query.language === SyntaxType.kuery) { + if (query.language === SyntaxType.kuery && queryParam !== '') { updateUrlParams({ query: '' }); } }, [query.language, query.query, queryParam, updateUrlParams]); @@ -112,17 +119,18 @@ export const useQueryBar = (): UseQueryBarUtils => { useDebounce( () => { - if (query.language === SyntaxType.kuery && !error && esFilters) { + if (query.language === SyntaxType.kuery && !error && esFilters && search !== query.query) { updateUrlParams({ search: query.query as string }); } - if (query.language === SyntaxType.text) { + if (query.language === SyntaxType.text && search !== '') { updateUrlParams({ search: '' }); } - if (query.language === SyntaxType.kuery && query.query === '') { + // this calls when it probably doesn't need to + if (query.language === SyntaxType.kuery && query.query === '' && search !== '') { updateUrlParams({ search: '' }); } }, - 250, + DEBOUNCE_INTERVAL, [esFilters, error] ); diff --git a/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx b/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx new file mode 100644 index 0000000000000..580160bac4012 --- /dev/null +++ b/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx @@ -0,0 +1,42 @@ +/* + * 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, { createContext, useContext, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { useFetcher } from '../../../observability/public'; +import { DataPublicPluginStart, IndexPattern } from '../../../../../src/plugins/data/public'; +import { selectDynamicSettings } from '../state/selectors'; +import { getDynamicSettings } from '../state/actions/dynamic_settings'; + +export const UptimeIndexPatternContext = createContext({} as IndexPattern); + +export const UptimeIndexPatternContextProvider: React.FC<{ data: DataPublicPluginStart }> = ({ + children, + data: { indexPatterns }, +}) => { + const { settings } = useSelector(selectDynamicSettings); + const dispatch = useDispatch(); + + useEffect(() => { + if (typeof settings === 'undefined') { + dispatch(getDynamicSettings()); + } + }, [dispatch, settings]); + + const heartbeatIndices = settings?.heartbeatIndices || ''; + + const { data } = useFetcher>(async () => { + if (heartbeatIndices) { + // this only creates an index pattern in memory, not as saved object + return indexPatterns.create({ title: heartbeatIndices }); + } + }, [heartbeatIndices]); + + return ; +}; + +export const useIndexPattern = () => useContext(UptimeIndexPatternContext); diff --git a/x-pack/plugins/uptime/public/hooks/__snapshots__/use_url_params.test.tsx.snap b/x-pack/plugins/uptime/public/hooks/__snapshots__/use_url_params.test.tsx.snap index 31b78a8f810f0..d8b148675dc62 100644 --- a/x-pack/plugins/uptime/public/hooks/__snapshots__/use_url_params.test.tsx.snap +++ b/x-pack/plugins/uptime/public/hooks/__snapshots__/use_url_params.test.tsx.snap @@ -210,7 +210,7 @@ exports[`useUrlParams deletes keys that do not have truthy values 1`] = ` } >
- {"pagination":"foo","absoluteDateRangeStart":20,"absoluteDateRangeEnd":20,"autorefreshInterval":60000,"autorefreshIsPaused":false,"dateRangeStart":"now-12","dateRangeEnd":"now","filters":"","search":"","statusFilter":"","focusConnectorField":false,"query":""} + {"pagination":"foo","absoluteDateRangeStart":20,"absoluteDateRangeEnd":20,"autorefreshInterval":60000,"autorefreshIsPaused":false,"dateRangeStart":"now-12","dateRangeEnd":"now","filters":"","excludedFilters":"","search":"","statusFilter":"","focusConnectorField":false,"query":""}