From 5256b6d23ef4d05ff3dce1d7d91711c1748c8d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Mon, 12 Jul 2021 11:46:38 +0200 Subject: [PATCH 01/40] Accomodate height of the (#104882) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../sections/agent_policy/edit_package_policy_page/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx index b07d76dc6bd8e8..ee529b6865e565 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx @@ -430,7 +430,9 @@ export const EditPackagePolicyForm = memo<{ /> )} {configurePackage} - + {/* Extra space to accomodate the EuiBottomBar height */} + + From b8dfcafe38e636ec88b2ca03253cf15cec844d27 Mon Sep 17 00:00:00 2001 From: Pete Harverson Date: Mon, 12 Jul 2021 11:31:54 +0100 Subject: [PATCH 02/40] [ML] Fixes unnecessary too many buckets warning on anomaly chart embeddable (#105043) * [ML] Fixes unnecessary too many buckets warning on anomaly chart embeddable * [ML] Update jest tests for number of axis ticks. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../explorer_chart_distribution.js | 24 ++++++++++--------- .../explorer_chart_distribution.test.js | 2 +- .../explorer_chart_single_metric.js | 24 ++++++++++--------- .../explorer_chart_single_metric.test.js | 2 +- .../anomaly_explorer_charts_service.ts | 11 +++++++-- 5 files changed, 37 insertions(+), 26 deletions(-) diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js index 7efd36bbe57c61..27a934fa841fe0 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js @@ -270,12 +270,6 @@ export class ExplorerChartDistribution extends React.Component { const tickValuesStart = Math.max(config.selectedEarliest, config.plotEarliest); // +1 ms to account for the ms that was subtracted for query aggregations. const interval = config.selectedLatest - config.selectedEarliest + 1; - const tickValues = getTickValues( - tickValuesStart, - interval, - config.plotEarliest, - config.plotLatest - ); const xAxis = d3.svg .axis() @@ -286,10 +280,18 @@ export class ExplorerChartDistribution extends React.Component { .tickPadding(10) .tickFormat((d) => moment(d).format(xAxisTickFormat)); - // With tooManyBuckets the chart would end up with no x-axis labels - // because the ticks are based on the span of the emphasis section, - // and the highlighted area spans the whole chart. - if (tooManyBuckets === false) { + // With tooManyBuckets, or when the chart is used as an embeddable, + // the chart would end up with no x-axis labels because the ticks are based on the span of the + // emphasis section, and the selected area spans the whole chart. + const useAutoTicks = + tooManyBuckets === true || interval >= config.plotLatest - config.plotEarliest; + if (useAutoTicks === false) { + const tickValues = getTickValues( + tickValuesStart, + interval, + config.plotEarliest, + config.plotLatest + ); xAxis.tickValues(tickValues); } else { xAxis.ticks(numTicksForDateFormat(vizWidth, xAxisTickFormat)); @@ -327,7 +329,7 @@ export class ExplorerChartDistribution extends React.Component { }); } - if (tooManyBuckets === false) { + if (useAutoTicks === false) { removeLabelOverlap(gAxis, tickValuesStart, interval, vizWidth); } } diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js index 11a15b192fc520..8d2f66a870c75c 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js @@ -139,7 +139,7 @@ describe('ExplorerChart', () => { expect(+selectedInterval.getAttribute('height')).toBe(166); const xAxisTicks = wrapper.getDOMNode().querySelector('.x').querySelectorAll('.tick'); - expect([...xAxisTicks]).toHaveLength(0); + expect([...xAxisTicks]).toHaveLength(1); const yAxisTicks = wrapper.getDOMNode().querySelector('.y').querySelectorAll('.tick'); expect([...yAxisTicks]).toHaveLength(5); const emphasizedAxisLabel = wrapper diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js index dd07a7d6cbdee0..19390017244a8c 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js @@ -196,12 +196,6 @@ export class ExplorerChartSingleMetric extends React.Component { const tickValuesStart = Math.max(config.selectedEarliest, config.plotEarliest); // +1 ms to account for the ms that was subtracted for query aggregations. const interval = config.selectedLatest - config.selectedEarliest + 1; - const tickValues = getTickValues( - tickValuesStart, - interval, - config.plotEarliest, - config.plotLatest - ); const xAxis = d3.svg .axis() @@ -212,10 +206,18 @@ export class ExplorerChartSingleMetric extends React.Component { .tickPadding(10) .tickFormat((d) => moment(d).format(xAxisTickFormat)); - // With tooManyBuckets the chart would end up with no x-axis labels - // because the ticks are based on the span of the emphasis section, - // and the highlighted area spans the whole chart. - if (tooManyBuckets === false) { + // With tooManyBuckets, or when the chart is used as an embeddable, + // the chart would end up with no x-axis labels because the ticks are based on the span of the + // emphasis section, and the selected area spans the whole chart. + const useAutoTicks = + tooManyBuckets === true || interval >= config.plotLatest - config.plotEarliest; + if (useAutoTicks === false) { + const tickValues = getTickValues( + tickValuesStart, + interval, + config.plotEarliest, + config.plotLatest + ); xAxis.tickValues(tickValues); } else { xAxis.ticks(numTicksForDateFormat(vizWidth, xAxisTickFormat)); @@ -243,7 +245,7 @@ export class ExplorerChartSingleMetric extends React.Component { axes.append('g').attr('class', 'y axis').call(yAxis); - if (tooManyBuckets === false) { + if (useAutoTicks === false) { removeLabelOverlap(gAxis, tickValuesStart, interval, vizWidth); } } diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js index 981f7515d3d706..00172965bc2162 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js @@ -144,7 +144,7 @@ describe('ExplorerChart', () => { expect(+selectedInterval.getAttribute('height')).toBe(166); const xAxisTicks = wrapper.getDOMNode().querySelector('.x').querySelectorAll('.tick'); - expect([...xAxisTicks]).toHaveLength(0); + expect([...xAxisTicks]).toHaveLength(1); const yAxisTicks = wrapper.getDOMNode().querySelector('.y').querySelectorAll('.tick'); expect([...yAxisTicks]).toHaveLength(10); diff --git a/x-pack/plugins/ml/public/application/services/anomaly_explorer_charts_service.ts b/x-pack/plugins/ml/public/application/services/anomaly_explorer_charts_service.ts index afad043fcc4d13..97ddefac860f2d 100644 --- a/x-pack/plugins/ml/public/application/services/anomaly_explorer_charts_service.ts +++ b/x-pack/plugins/ml/public/application/services/anomaly_explorer_charts_service.ts @@ -225,13 +225,20 @@ export class AnomalyExplorerChartsService { chartRange.min = chartRange.min + maxBucketSpanMs; } + // When used as an embeddable, selectedEarliestMs is the start date on the time picker, + // which may be earlier than the time of the first point plotted in the chart (as we plot + // the first full bucket with a start date no earlier than the start). + const selectedEarliestBucketCeil = boundsMin + ? Math.ceil(Math.max(selectedEarliestMs, boundsMin) / maxBucketSpanMs) * maxBucketSpanMs + : Math.ceil(selectedEarliestMs / maxBucketSpanMs) * maxBucketSpanMs; + const selectedLatestBucketStart = boundsMax ? Math.floor(Math.min(selectedLatestMs, boundsMax) / maxBucketSpanMs) * maxBucketSpanMs : Math.floor(selectedLatestMs / maxBucketSpanMs) * maxBucketSpanMs; if ( - (chartRange.min > selectedEarliestMs || chartRange.max < selectedLatestBucketStart) && - chartRange.max - chartRange.min < selectedLatestBucketStart - selectedEarliestMs + (chartRange.min > selectedEarliestBucketCeil || chartRange.max < selectedLatestBucketStart) && + chartRange.max - chartRange.min < selectedLatestBucketStart - selectedEarliestBucketCeil ) { tooManyBuckets = true; } From 216bb5e1b8c8406cd23503619972e5a3ea543790 Mon Sep 17 00:00:00 2001 From: Dzmitry Lemechko Date: Mon, 12 Jul 2021 13:23:08 +0200 Subject: [PATCH 03/40] [load] run puppeteer script before gatling scenarios (#104836) * [load] puppeteer script before load testing * install dependencies after metricbeat configuration Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- test/scripts/jenkins_build_load_testing.sh | 3 +++ x-pack/test/load/runner.ts | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/test/scripts/jenkins_build_load_testing.sh b/test/scripts/jenkins_build_load_testing.sh index d7c7bda83c9ef2..667540515fc835 100755 --- a/test/scripts/jenkins_build_load_testing.sh +++ b/test/scripts/jenkins_build_load_testing.sh @@ -53,6 +53,9 @@ echo "cloud.auth: ${USER_FROM_VAULT}:${PASS_FROM_VAULT}" >> cfg/metricbeat/metri cp cfg/metricbeat/metricbeat.yml $KIBANA_DIR/metricbeat-install/metricbeat.yml # Disable system monitoring: enabled for now to have more data #mv $KIBANA_DIR/metricbeat-install/modules.d/system.yml $KIBANA_DIR/metricbeat-install/modules.d/system.yml.disabled +echo " -> Building puppeteer project" +cd puppeteer +yarn install && yarn build popd # doesn't persist, also set in kibanaPipeline.groovy diff --git a/x-pack/test/load/runner.ts b/x-pack/test/load/runner.ts index 2d379391b20897..0bea5992f55394 100644 --- a/x-pack/test/load/runner.ts +++ b/x-pack/test/load/runner.ts @@ -18,6 +18,7 @@ const simulationPackage = 'org.kibanaLoadTest.simulation'; const simulationFIleExtension = '.scala'; const gatlingProjectRootPath: string = process.env.GATLING_PROJECT_PATH || resolve(REPO_ROOT, '../kibana-load-testing'); +const puppeteerProjectRootPath: string = resolve(gatlingProjectRootPath, 'puppeteer'); const simulationEntry: string = process.env.GATLING_SIMULATIONS || 'branch.DemoJourney'; if (!Fs.existsSync(gatlingProjectRootPath)) { @@ -52,6 +53,15 @@ export async function GatlingTestRunner({ getService }: FtrProviderContext) { const log = getService('log'); await withProcRunner(log, async (procs) => { + await procs.run('node build/index.js', { + cmd: 'node', + args: ['build/index.js'], + cwd: puppeteerProjectRootPath, + env: { + ...process.env, + }, + wait: true, + }); for (let i = 0; i < simulationClasses.length; i++) { await procs.run('gatling: test', { cmd: 'mvn', From 832d349930be1f8d4a7a47733f413b0ab75303e8 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 12 Jul 2021 14:13:42 +0200 Subject: [PATCH 04/40] [KibanaLegacy] Remove unused stuff and make things async if it is easy (#104638) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * remove unused stuff and make things async if it is easy * fix problems * load bootstrap in monitoring * load angular bootstrap for saved searches and in unit tests * fix vis_type_table tests * Update x-pack/plugins/monitoring/public/plugin.ts Co-authored-by: Ester Martí Vilaseca * Update x-pack/plugins/monitoring/public/plugin.ts Co-authored-by: Ester Martí Vilaseca Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Stratoula Kalafateli Co-authored-by: Ester Martí Vilaseca --- .../doc_table/components/row_headers.test.js | 4 + .../angular/doc_table/doc_table.test.js | 4 + .../application/angular/get_inner_angular.ts | 4 +- .../application/angular/helpers/index.ts | 1 + .../angular/helpers}/promises.d.ts | 0 .../application/angular/helpers}/promises.js | 0 src/plugins/discover/public/plugin.tsx | 2 + .../public/angular/angular_config.tsx | 7 +- .../kibana_legacy/public/angular/index.ts | 2 - src/plugins/kibana_legacy/public/index.ts | 1 - src/plugins/kibana_legacy/public/mocks.ts | 1 + .../kibana_legacy/public/notify/index.ts | 1 - .../notify/toasts/TOAST_NOTIFICATIONS.md | 100 ------------------ .../public/notify/toasts/index.ts | 9 -- .../notify/toasts/toast_notifications.test.ts | 76 ------------- .../notify/toasts/toast_notifications.ts | 37 ------- src/plugins/kibana_legacy/public/plugin.ts | 8 ++ .../kibana_legacy/public/utils/index.ts | 1 - .../kibana_legacy/public/utils/system_api.ts | 40 ------- src/plugins/timelion/public/plugin.ts | 5 +- .../public/legacy/agg_table/agg_table.test.js | 6 +- .../legacy/agg_table/agg_table_group.test.js | 6 +- .../public/legacy/get_inner_angular.ts | 3 - .../paginated_table/paginated_table.test.ts | 5 + .../legacy/table_vis_controller.test.ts | 4 + .../public/legacy/vis_controller.ts | 1 + x-pack/plugins/graph/public/plugin.ts | 7 +- x-pack/plugins/monitoring/public/plugin.ts | 5 +- 28 files changed, 54 insertions(+), 286 deletions(-) rename src/plugins/{kibana_legacy/public/angular => discover/public/application/angular/helpers}/promises.d.ts (100%) rename src/plugins/{kibana_legacy/public/angular => discover/public/application/angular/helpers}/promises.js (100%) delete mode 100644 src/plugins/kibana_legacy/public/notify/toasts/TOAST_NOTIFICATIONS.md delete mode 100644 src/plugins/kibana_legacy/public/notify/toasts/index.ts delete mode 100644 src/plugins/kibana_legacy/public/notify/toasts/toast_notifications.test.ts delete mode 100644 src/plugins/kibana_legacy/public/notify/toasts/toast_notifications.ts delete mode 100644 src/plugins/kibana_legacy/public/utils/system_api.ts diff --git a/src/plugins/discover/public/application/angular/doc_table/components/row_headers.test.js b/src/plugins/discover/public/application/angular/doc_table/components/row_headers.test.js index a087ac86971838..1a3b34c45d05e2 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/row_headers.test.js +++ b/src/plugins/discover/public/application/angular/doc_table/components/row_headers.test.js @@ -19,6 +19,7 @@ import { setScopedHistory, setServices, setDocViewsRegistry } from '../../../../ import { coreMock } from '../../../../../../../core/public/mocks'; import { dataPluginMock } from '../../../../../../data/public/mocks'; import { navigationPluginMock } from '../../../../../../navigation/public/mocks'; +import { initAngularBootstrap } from '../../../../../../kibana_legacy/public/angular_bootstrap'; import { getInnerAngularModule } from '../../get_inner_angular'; import { createBrowserHistory } from 'history'; @@ -41,6 +42,9 @@ describe('Doc Table', () => { // Stub out a minimal mapping of 4 fields let mapping; + beforeAll(async () => { + await initAngularBootstrap(); + }); beforeAll(() => setScopedHistory(createBrowserHistory())); beforeEach(() => { angular.element.prototype.slice = jest.fn(function (index) { diff --git a/src/plugins/discover/public/application/angular/doc_table/doc_table.test.js b/src/plugins/discover/public/application/angular/doc_table/doc_table.test.js index 1db35ddf18089e..097f32965b1413 100644 --- a/src/plugins/discover/public/application/angular/doc_table/doc_table.test.js +++ b/src/plugins/discover/public/application/angular/doc_table/doc_table.test.js @@ -17,6 +17,7 @@ import hits from '../../../__fixtures__/real_hits'; import { coreMock } from '../../../../../../core/public/mocks'; import { dataPluginMock } from '../../../../../data/public/mocks'; import { navigationPluginMock } from '../../../../../navigation/public/mocks'; +import { initAngularBootstrap } from '../../../../../kibana_legacy/public/angular_bootstrap'; import { setScopedHistory, setServices } from '../../../kibana_services'; import { getInnerAngularModule } from '../get_inner_angular'; @@ -54,6 +55,9 @@ describe('docTable', () => { const core = coreMock.createStart(); let $elem; + beforeAll(async () => { + await initAngularBootstrap(); + }); beforeAll(() => setScopedHistory(createBrowserHistory())); beforeEach(() => { angular.element.prototype.slice = jest.fn(() => { diff --git a/src/plugins/discover/public/application/angular/get_inner_angular.ts b/src/plugins/discover/public/application/angular/get_inner_angular.ts index 26d64d5adc8a33..992d82795302b9 100644 --- a/src/plugins/discover/public/application/angular/get_inner_angular.ts +++ b/src/plugins/discover/public/application/angular/get_inner_angular.ts @@ -33,13 +33,12 @@ import { createDocViewerDirective } from './doc_viewer'; import { createDiscoverGridDirective } from './create_discover_grid_directive'; import { createRenderCompleteDirective } from './directives/render_complete'; import { - initAngularBootstrap, configureAppAngularModule, PrivateProvider, - PromiseServiceCreator, registerListenEventListener, watchMultiDecorator, } from '../../../../kibana_legacy/public'; +import { PromiseServiceCreator } from './helpers'; import { DiscoverStartPlugins } from '../../plugin'; import { getScopedHistory } from '../../kibana_services'; import { createDiscoverDirective } from './create_discover_directive'; @@ -54,7 +53,6 @@ export function getInnerAngularModule( deps: DiscoverStartPlugins, context: PluginInitializerContext ) { - initAngularBootstrap(); const module = initializeInnerAngularModule(name, core, deps.navigation, deps.data); configureAppAngularModule(module, { core, env: context.env }, true, getScopedHistory); return module; diff --git a/src/plugins/discover/public/application/angular/helpers/index.ts b/src/plugins/discover/public/application/angular/helpers/index.ts index 3d2c0b1c63b332..6a7f75b7e81a20 100644 --- a/src/plugins/discover/public/application/angular/helpers/index.ts +++ b/src/plugins/discover/public/application/angular/helpers/index.ts @@ -8,3 +8,4 @@ export { formatRow, formatTopLevelObject } from './row_formatter'; export { handleSourceColumnState } from './state_helpers'; +export { PromiseServiceCreator } from './promises'; diff --git a/src/plugins/kibana_legacy/public/angular/promises.d.ts b/src/plugins/discover/public/application/angular/helpers/promises.d.ts similarity index 100% rename from src/plugins/kibana_legacy/public/angular/promises.d.ts rename to src/plugins/discover/public/application/angular/helpers/promises.d.ts diff --git a/src/plugins/kibana_legacy/public/angular/promises.js b/src/plugins/discover/public/application/angular/helpers/promises.js similarity index 100% rename from src/plugins/kibana_legacy/public/angular/promises.js rename to src/plugins/discover/public/application/angular/helpers/promises.js diff --git a/src/plugins/discover/public/plugin.tsx b/src/plugins/discover/public/plugin.tsx index 3e31fe1d46d459..1e8a5cdac95efe 100644 --- a/src/plugins/discover/public/plugin.tsx +++ b/src/plugins/discover/public/plugin.tsx @@ -403,6 +403,7 @@ export class DiscoverPlugin } // this is used by application mount and tests const { getInnerAngularModule } = await import('./application/angular/get_inner_angular'); + await plugins.kibanaLegacy.loadAngularBootstrap(); const module = getInnerAngularModule( innerAngularName, core, @@ -473,6 +474,7 @@ export class DiscoverPlugin throw Error('Discover plugin getEmbeddableInjector: initializeServices is undefined'); } const { core, plugins } = await this.initializeServices(); + await getServices().kibanaLegacy.loadAngularBootstrap(); getServices().kibanaLegacy.loadFontAwesome(); const { getInnerAngularModuleEmbeddable } = await import( './application/angular/get_inner_angular' diff --git a/src/plugins/kibana_legacy/public/angular/angular_config.tsx b/src/plugins/kibana_legacy/public/angular/angular_config.tsx index daecfbc57ea991..48ee6d2db269e6 100644 --- a/src/plugins/kibana_legacy/public/angular/angular_config.tsx +++ b/src/plugins/kibana_legacy/public/angular/angular_config.tsx @@ -13,6 +13,7 @@ import { ILocationProvider, IModule, IRootScopeService, + IRequestConfig, } from 'angular'; import $ from 'jquery'; import { set } from '@elastic/safer-lodash-set'; @@ -22,7 +23,6 @@ import { ChromeBreadcrumb, EnvironmentMode, PackageInfo } from 'kibana/public'; import { History } from 'history'; import { CoreStart } from 'kibana/public'; -import { isSystemApiRequest } from '../utils'; import { formatAngularHttpError, isAngularHttpError } from '../notify/lib'; export interface RouteConfiguration { @@ -38,6 +38,11 @@ export interface RouteConfiguration { requireUICapability?: string; } +function isSystemApiRequest(request: IRequestConfig) { + const { headers } = request; + return headers && !!headers['kbn-system-request']; +} + /** * Detects whether a given angular route is a dummy route that doesn't * require any action. There are two ways this can happen: diff --git a/src/plugins/kibana_legacy/public/angular/index.ts b/src/plugins/kibana_legacy/public/angular/index.ts index d9d8c0c19eb7b1..369495698591d0 100644 --- a/src/plugins/kibana_legacy/public/angular/index.ts +++ b/src/plugins/kibana_legacy/public/angular/index.ts @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -// @ts-ignore -export { PromiseServiceCreator } from './promises'; // @ts-ignore export { watchMultiDecorator } from './watch_multi'; export * from './angular_config'; diff --git a/src/plugins/kibana_legacy/public/index.ts b/src/plugins/kibana_legacy/public/index.ts index 03adb768cde208..ea5172f78a68f3 100644 --- a/src/plugins/kibana_legacy/public/index.ts +++ b/src/plugins/kibana_legacy/public/index.ts @@ -14,7 +14,6 @@ export const plugin = (initializerContext: PluginInitializerContext) => export * from './plugin'; -export { initAngularBootstrap } from './angular_bootstrap'; export { PaginateDirectiveProvider, PaginateControlsDirectiveProvider } from './paginate/paginate'; export * from './angular'; export * from './notify'; diff --git a/src/plugins/kibana_legacy/public/mocks.ts b/src/plugins/kibana_legacy/public/mocks.ts index 40834635cc5704..6116c0682cb3bb 100644 --- a/src/plugins/kibana_legacy/public/mocks.ts +++ b/src/plugins/kibana_legacy/public/mocks.ts @@ -22,6 +22,7 @@ const createStartContract = (): Start => ({ getHideWriteControls: jest.fn(), }, loadFontAwesome: jest.fn(), + loadAngularBootstrap: jest.fn(), }); export const kibanaLegacyPluginMock = { diff --git a/src/plugins/kibana_legacy/public/notify/index.ts b/src/plugins/kibana_legacy/public/notify/index.ts index a243059cb19183..d4dcaa77cc47ad 100644 --- a/src/plugins/kibana_legacy/public/notify/index.ts +++ b/src/plugins/kibana_legacy/public/notify/index.ts @@ -6,5 +6,4 @@ * Side Public License, v 1. */ -export * from './toasts'; export * from './lib'; diff --git a/src/plugins/kibana_legacy/public/notify/toasts/TOAST_NOTIFICATIONS.md b/src/plugins/kibana_legacy/public/notify/toasts/TOAST_NOTIFICATIONS.md deleted file mode 100644 index de6a51f3927d17..00000000000000 --- a/src/plugins/kibana_legacy/public/notify/toasts/TOAST_NOTIFICATIONS.md +++ /dev/null @@ -1,100 +0,0 @@ -# Toast notifications - -Use this service to surface toasts in the bottom-right corner of the screen. After a brief delay, they'll disappear. They're useful for notifying the user of state changes. See [the EUI docs](https://elastic.github.io/eui/) for more information on toasts and their role within the UI. - -## Importing the module - -```js -import { toastNotifications } from 'ui/notify'; -``` - -## Interface - -### Adding toasts - -For convenience, there are several methods which predefine the appearance of different types of toasts. Use these methods so that the same types of toasts look similar to the user. - -#### Default - -Neutral toast. Tell the user a change in state has occurred, which is not necessarily good or bad. - -```js -toastNotifications.add('Copied to clipboard'); -``` - -#### Success - -Let the user know that an action was successful, such as saving or deleting an object. - -```js -toastNotifications.addSuccess('Your document was saved'); -``` - -#### Warning - -If something OK or good happened, but perhaps wasn't perfect, show a warning toast. - -```js -toastNotifications.addWarning('Your document was saved, but not its edit history'); -``` - -#### Danger - -When the user initiated an action but the action failed, show them a danger toast. - -```js -toastNotifications.addDanger('An error caused your document to be lost'); -``` - -### Removing a toast - -Toasts will automatically be dismissed after a brief delay, but if for some reason you want to dismiss a toast, you can use the returned toast from one of the `add` methods and then pass it to `remove`. - -```js -const toast = toastNotifications.add('Your document was saved'); -toastNotifications.remove(toast); -``` - -### Configuration options - -If you want to configure the toast further you can provide an object instead of a string. The properties of this object correspond to the `propTypes` accepted by the `EuiToast` component. Refer to [the EUI docs](https://elastic.github.io/eui/) for info on these `propTypes`. - -```js -toastNotifications.add({ - title: 'Your document was saved', - text: 'Only you have access to this document', - color: 'success', - iconType: 'check', - 'data-test-subj': 'saveDocumentSuccess', -}); -``` - -Because the underlying components are React, you can use JSX to pass in React elements to the `text` prop. This gives you total flexibility over the content displayed within the toast. - -```js -toastNotifications.add({ - title: 'Your document was saved', - text: ( -
-

- Only you have access to this document. Edit permissions. -

- - -
- ), -}); -``` - -## Use in functional tests - -Functional tests are commonly used to verify that a user action yielded a successful outcome. If you surface a toast to notify the user of this successful outcome, you can place a `data-test-subj` attribute on the toast and use it to check if the toast exists inside of your functional test. This acts as a proxy for verifying the successful outcome. - -```js -toastNotifications.addSuccess({ - title: 'Your document was saved', - 'data-test-subj': 'saveDocumentSuccess', -}); -``` diff --git a/src/plugins/kibana_legacy/public/notify/toasts/index.ts b/src/plugins/kibana_legacy/public/notify/toasts/index.ts deleted file mode 100644 index cdd7df04548fbb..00000000000000 --- a/src/plugins/kibana_legacy/public/notify/toasts/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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -export { ToastNotifications } from './toast_notifications'; diff --git a/src/plugins/kibana_legacy/public/notify/toasts/toast_notifications.test.ts b/src/plugins/kibana_legacy/public/notify/toasts/toast_notifications.test.ts deleted file mode 100644 index c2c5d9a4fc014a..00000000000000 --- a/src/plugins/kibana_legacy/public/notify/toasts/toast_notifications.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { notificationServiceMock } from '../../../../../core/public/mocks'; -import { ToastNotifications } from './toast_notifications'; -import { Toast } from 'kibana/public'; -import { BehaviorSubject } from 'rxjs'; - -describe('ToastNotifications', () => { - describe('interface', () => { - function setup() { - const toastsMock = notificationServiceMock.createStartContract().toasts; - return { toastNotifications: new ToastNotifications(toastsMock), toastsMock }; - } - - describe('add method', () => { - test('adds a toast', () => { - const { toastNotifications, toastsMock } = setup(); - toastNotifications.add({}); - expect(toastsMock.add).toHaveBeenCalled(); - }); - }); - - describe('remove method', () => { - test('removes a toast', () => { - const { toastNotifications, toastsMock } = setup(); - const fakeToast = {} as Toast; - toastNotifications.remove(fakeToast); - expect(toastsMock.remove).toHaveBeenCalledWith(fakeToast); - }); - }); - - describe('onChange method', () => { - test('callback is called when observable changes', () => { - const toastsMock = notificationServiceMock.createStartContract().toasts; - const toasts$ = new BehaviorSubject([]); - toastsMock.get$.mockReturnValue(toasts$); - const toastNotifications = new ToastNotifications(toastsMock); - const onChangeSpy = jest.fn(); - toastNotifications.onChange(onChangeSpy); - toasts$.next([{ id: 'toast1' }]); - toasts$.next([]); - expect(onChangeSpy).toHaveBeenCalledTimes(2); - }); - }); - - describe('addSuccess method', () => { - test('adds a success toast', () => { - const { toastNotifications, toastsMock } = setup(); - toastNotifications.addSuccess({}); - expect(toastsMock.addSuccess).toHaveBeenCalled(); - }); - }); - - describe('addWarning method', () => { - test('adds a warning toast', () => { - const { toastNotifications, toastsMock } = setup(); - toastNotifications.addWarning({}); - expect(toastsMock.addWarning).toHaveBeenCalled(); - }); - }); - - describe('addDanger method', () => { - test('adds a danger toast', () => { - const { toastNotifications, toastsMock } = setup(); - toastNotifications.addWarning({}); - expect(toastsMock.addWarning).toHaveBeenCalled(); - }); - }); - }); -}); diff --git a/src/plugins/kibana_legacy/public/notify/toasts/toast_notifications.ts b/src/plugins/kibana_legacy/public/notify/toasts/toast_notifications.ts deleted file mode 100644 index e7ccbbca07b734..00000000000000 --- a/src/plugins/kibana_legacy/public/notify/toasts/toast_notifications.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { NotificationsSetup, Toast, ToastInput, ErrorToastOptions } from 'kibana/public'; - -export class ToastNotifications { - public list: Toast[] = []; - - private onChangeCallback?: () => void; - - constructor(private readonly toasts: NotificationsSetup['toasts']) { - toasts.get$().subscribe((list) => { - this.list = list; - - if (this.onChangeCallback) { - this.onChangeCallback(); - } - }); - } - - public onChange = (callback: () => void) => { - this.onChangeCallback = callback; - }; - - public add = (toastOrTitle: ToastInput) => this.toasts.add(toastOrTitle); - public remove = (toast: Toast) => this.toasts.remove(toast); - public addSuccess = (toastOrTitle: ToastInput) => this.toasts.addSuccess(toastOrTitle); - public addWarning = (toastOrTitle: ToastInput) => this.toasts.addWarning(toastOrTitle); - public addDanger = (toastOrTitle: ToastInput) => this.toasts.addDanger(toastOrTitle); - public addError = (error: Error, options: ErrorToastOptions) => - this.toasts.addError(error, options); -} diff --git a/src/plugins/kibana_legacy/public/plugin.ts b/src/plugins/kibana_legacy/public/plugin.ts index 337fdb80da7e45..f60130d367b584 100644 --- a/src/plugins/kibana_legacy/public/plugin.ts +++ b/src/plugins/kibana_legacy/public/plugin.ts @@ -33,6 +33,14 @@ export class KibanaLegacyPlugin { loadFontAwesome: async () => { await import('./font_awesome'); }, + /** + * Loads angular bootstrap modules. Should be removed once the last consumer has migrated to EUI + * @deprecated + */ + loadAngularBootstrap: async () => { + const { initAngularBootstrap } = await import('./angular_bootstrap'); + initAngularBootstrap(); + }, /** * @deprecated * Just exported for wiring up with dashboard mode, should not be used. diff --git a/src/plugins/kibana_legacy/public/utils/index.ts b/src/plugins/kibana_legacy/public/utils/index.ts index db3c0af6c8cb94..94233558b4627c 100644 --- a/src/plugins/kibana_legacy/public/utils/index.ts +++ b/src/plugins/kibana_legacy/public/utils/index.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -export * from './system_api'; // @ts-ignore export { KbnAccessibleClickProvider } from './kbn_accessible_click'; // @ts-ignore diff --git a/src/plugins/kibana_legacy/public/utils/system_api.ts b/src/plugins/kibana_legacy/public/utils/system_api.ts deleted file mode 100644 index d0fe221935ba51..00000000000000 --- a/src/plugins/kibana_legacy/public/utils/system_api.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { IRequestConfig } from 'angular'; - -const SYSTEM_REQUEST_HEADER_NAME = 'kbn-system-request'; -const LEGACY_SYSTEM_API_HEADER_NAME = 'kbn-system-api'; - -/** - * Adds a custom header designating request as system API - * @param originalHeaders Object representing set of headers - * @return Object representing set of headers, with system API header added in - */ -export function addSystemApiHeader(originalHeaders: Record) { - const systemApiHeaders = { - [SYSTEM_REQUEST_HEADER_NAME]: true, - }; - return { - ...originalHeaders, - ...systemApiHeaders, - }; -} - -/** - * Returns true if request is a system API request; false otherwise - * - * @param request Object Request object created by $http service - * @return true if request is a system API request; false otherwise - */ -export function isSystemApiRequest(request: IRequestConfig) { - const { headers } = request; - return ( - headers && (!!headers[SYSTEM_REQUEST_HEADER_NAME] || !!headers[LEGACY_SYSTEM_API_HEADER_NAME]) - ); -} diff --git a/src/plugins/timelion/public/plugin.ts b/src/plugins/timelion/public/plugin.ts index 6f8dbfdcc67041..63ea9a38e2795d 100644 --- a/src/plugins/timelion/public/plugin.ts +++ b/src/plugins/timelion/public/plugin.ts @@ -19,7 +19,7 @@ import { AppNavLinkStatus, } from '../../../core/public'; import { Panel } from './panels/panel'; -import { initAngularBootstrap } from '../../kibana_legacy/public'; +import { KibanaLegacyStart } from '../../kibana_legacy/public'; import { createKbnUrlTracker } from '../../kibana_utils/public'; import { DataPublicPluginStart, esFilters, DataPublicPluginSetup } from '../../data/public'; import { NavigationPublicPluginStart } from '../../navigation/public'; @@ -41,6 +41,7 @@ export interface TimelionPluginStartDependencies { visualizations: VisualizationsStart; visTypeTimelion: VisTypeTimelionPluginStart; savedObjects: SavedObjectsStart; + kibanaLegacy: KibanaLegacyStart; } /** @internal */ @@ -91,7 +92,6 @@ export class TimelionPlugin stopUrlTracker(); }; - initAngularBootstrap(); core.application.register({ id: 'timelion', title: 'Timelion', @@ -103,6 +103,7 @@ export class TimelionPlugin visTypeTimelion.isUiEnabled === false ? AppNavLinkStatus.hidden : AppNavLinkStatus.default, mount: async (params: AppMountParameters) => { const [coreStart, pluginsStart] = await core.getStartServices(); + await pluginsStart.kibanaLegacy.loadAngularBootstrap(); this.currentHistory = params.history; appMounted(); diff --git a/src/plugins/vis_type_table/public/legacy/agg_table/agg_table.test.js b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table.test.js index 65e26ddf6e03fd..cbc3db6585a7da 100644 --- a/src/plugins/vis_type_table/public/legacy/agg_table/agg_table.test.js +++ b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table.test.js @@ -15,7 +15,7 @@ import { round } from 'lodash'; import { getFieldFormatsRegistry } from '../../../../data/public/test_utils'; import { coreMock } from '../../../../../core/public/mocks'; -import { initAngularBootstrap } from '../../../../kibana_legacy/public'; +import { initAngularBootstrap } from '../../../../kibana_legacy/public/angular_bootstrap'; import { setUiSettings } from '../../../../data/public/services'; import { UI_SETTINGS } from '../../../../data/public/'; import { CSV_SEPARATOR_SETTING, CSV_QUOTE_VALUES_SETTING } from '../../../../share/public'; @@ -60,10 +60,12 @@ describe('Table Vis - AggTable Directive', function () { initTableVisLegacyModule(tableVisModule); }; + beforeAll(async () => { + await initAngularBootstrap(); + }); beforeEach(() => { setUiSettings(core.uiSettings); setFormatService(getFieldFormatsRegistry(core)); - initAngularBootstrap(); initLocalAngular(); angular.mock.module('kibana/table_vis'); angular.mock.inject(($injector, config) => { diff --git a/src/plugins/vis_type_table/public/legacy/agg_table/agg_table_group.test.js b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table_group.test.js index 1c6630e30e5f73..ba04b2f449f6dd 100644 --- a/src/plugins/vis_type_table/public/legacy/agg_table/agg_table_group.test.js +++ b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table_group.test.js @@ -13,11 +13,11 @@ import expect from '@kbn/expect'; import { getFieldFormatsRegistry } from '../../../../data/public/test_utils'; import { coreMock } from '../../../../../core/public/mocks'; -import { initAngularBootstrap } from '../../../../kibana_legacy/public'; import { setUiSettings } from '../../../../data/public/services'; import { setFormatService } from '../../services'; import { getInnerAngular } from '../get_inner_angular'; import { initTableVisLegacyModule } from '../table_vis_legacy_module'; +import { initAngularBootstrap } from '../../../../kibana_legacy/public/angular_bootstrap'; import { tabifiedData } from './tabified_data'; const uiSettings = new Map(); @@ -40,10 +40,12 @@ describe('Table Vis - AggTableGroup Directive', function () { initTableVisLegacyModule(tableVisModule); }; + beforeAll(async () => { + await initAngularBootstrap(); + }); beforeEach(() => { setUiSettings(core.uiSettings); setFormatService(getFieldFormatsRegistry(core)); - initAngularBootstrap(); initLocalAngular(); angular.mock.module('kibana/table_vis'); angular.mock.inject(($injector) => { diff --git a/src/plugins/vis_type_table/public/legacy/get_inner_angular.ts b/src/plugins/vis_type_table/public/legacy/get_inner_angular.ts index 09fde318ee4dff..412dd904a5e872 100644 --- a/src/plugins/vis_type_table/public/legacy/get_inner_angular.ts +++ b/src/plugins/vis_type_table/public/legacy/get_inner_angular.ts @@ -16,7 +16,6 @@ import 'angular-recursion'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; import { CoreStart, IUiSettingsClient, PluginInitializerContext } from 'kibana/public'; import { - initAngularBootstrap, PaginateDirectiveProvider, PaginateControlsDirectiveProvider, PrivateProvider, @@ -24,8 +23,6 @@ import { KbnAccessibleClickProvider, } from '../../../kibana_legacy/public'; -initAngularBootstrap(); - const thirdPartyAngularDependencies = ['ngSanitize', 'ui.bootstrap', 'RecursionHelper']; export function getAngularModule(name: string, core: CoreStart, context: PluginInitializerContext) { diff --git a/src/plugins/vis_type_table/public/legacy/paginated_table/paginated_table.test.ts b/src/plugins/vis_type_table/public/legacy/paginated_table/paginated_table.test.ts index 77148803e7978b..3feff52f86792b 100644 --- a/src/plugins/vis_type_table/public/legacy/paginated_table/paginated_table.test.ts +++ b/src/plugins/vis_type_table/public/legacy/paginated_table/paginated_table.test.ts @@ -12,6 +12,7 @@ import $ from 'jquery'; import 'angular-sanitize'; import 'angular-mocks'; +import { initAngularBootstrap } from '../../../../kibana_legacy/public/angular_bootstrap'; import { getAngularModule } from '../get_inner_angular'; import { initTableVisLegacyModule } from '../table_vis_legacy_module'; import { coreMock } from '../../../../../core/public/mocks'; @@ -56,6 +57,10 @@ describe('Table Vis - Paginated table', () => { const defaultPerPage = 10; let paginatedTable: any; + beforeAll(async () => { + await initAngularBootstrap(); + }); + const initLocalAngular = () => { const tableVisModule = getAngularModule( 'kibana/table_vis', diff --git a/src/plugins/vis_type_table/public/legacy/table_vis_controller.test.ts b/src/plugins/vis_type_table/public/legacy/table_vis_controller.test.ts index 36a9cc9cce77fe..f4a742ea16cb4f 100644 --- a/src/plugins/vis_type_table/public/legacy/table_vis_controller.test.ts +++ b/src/plugins/vis_type_table/public/legacy/table_vis_controller.test.ts @@ -13,6 +13,7 @@ import $ from 'jquery'; import { getAngularModule } from './get_inner_angular'; import { initTableVisLegacyModule } from './table_vis_legacy_module'; +import { initAngularBootstrap } from '../../../kibana_legacy/public/angular_bootstrap'; import { tableVisLegacyTypeDefinition } from './table_vis_legacy_type'; import { Vis } from '../../../visualizations/public'; import { stubFields } from '../../../data/public/stubs'; @@ -76,6 +77,9 @@ describe('Table Vis - Controller', () => { initTableVisLegacyModule(tableVisModule); }; + beforeAll(async () => { + await initAngularBootstrap(); + }); beforeEach(initLocalAngular); beforeEach(angular.mock.module('kibana/table_vis')); diff --git a/src/plugins/vis_type_table/public/legacy/vis_controller.ts b/src/plugins/vis_type_table/public/legacy/vis_controller.ts index ee446c58c00139..ec198aa96f1f96 100644 --- a/src/plugins/vis_type_table/public/legacy/vis_controller.ts +++ b/src/plugins/vis_type_table/public/legacy/vis_controller.ts @@ -56,6 +56,7 @@ export function getTableVisualizationControllerClass( async initLocalAngular() { if (!this.tableVisModule) { const [coreStart, { kibanaLegacy }] = await core.getStartServices(); + await kibanaLegacy.loadAngularBootstrap(); this.tableVisModule = getAngularModule(innerAngularName, coreStart, context); initTableVisLegacyModule(this.tableVisModule); kibanaLegacy.loadFontAwesome(); diff --git a/x-pack/plugins/graph/public/plugin.ts b/x-pack/plugins/graph/public/plugin.ts index 4525b42b3feb45..ec19e639b91c96 100644 --- a/x-pack/plugins/graph/public/plugin.ts +++ b/x-pack/plugins/graph/public/plugin.ts @@ -19,10 +19,7 @@ import { } from '../../../../src/core/public'; import { Storage } from '../../../../src/plugins/kibana_utils/public'; -import { - initAngularBootstrap, - KibanaLegacyStart, -} from '../../../../src/plugins/kibana_legacy/public'; +import { KibanaLegacyStart } from '../../../../src/plugins/kibana_legacy/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../src/plugins/navigation/public'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; @@ -77,7 +74,6 @@ export class GraphPlugin const config = this.initializerContext.config.get(); - initAngularBootstrap(); core.application.register({ id: 'graph', title: 'Graph', @@ -88,6 +84,7 @@ export class GraphPlugin updater$: this.appUpdater$, mount: async (params: AppMountParameters) => { const [coreStart, pluginsStart] = await core.getStartServices(); + await pluginsStart.kibanaLegacy.loadAngularBootstrap(); coreStart.chrome.docTitle.change( i18n.translate('xpack.graph.pageTitle', { defaultMessage: 'Graph' }) ); diff --git a/x-pack/plugins/monitoring/public/plugin.ts b/x-pack/plugins/monitoring/public/plugin.ts index a5b7d4906b5869..9f84165a27ba9f 100644 --- a/x-pack/plugins/monitoring/public/plugin.ts +++ b/x-pack/plugins/monitoring/public/plugin.ts @@ -93,7 +93,10 @@ export class MonitoringPlugin category: DEFAULT_APP_CATEGORIES.management, mount: async (params: AppMountParameters) => { const [coreStart, pluginsStart] = await core.getStartServices(); - const { AngularApp } = await import('./angular'); + const [, { AngularApp }] = await Promise.all([ + pluginsStart.kibanaLegacy.loadAngularBootstrap(), + import('./angular'), + ]); const deps: MonitoringStartPluginDependencies = { navigation: pluginsStart.navigation, kibanaLegacy: pluginsStart.kibanaLegacy, From 87066e06b3cfe238b021f786896afdfd74b86303 Mon Sep 17 00:00:00 2001 From: Diana Derevyankina <54894989+DziyanaDzeraviankina@users.noreply.github.com> Date: Mon, 12 Jul 2021 17:25:52 +0300 Subject: [PATCH 05/40] [TSVB] Top_hit supports runtime fields (#103401) * [TSVB] Refactor top-hit aggregation to work with fields instead of _source * Allow select date strings for top_hit aggregation in table, metric, and markdown * Fix agg_with handling for top_hit and add some tests * Refactor get_agg_value and fix type check for _tsvb_chart * Refactor top_hit.js Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../application/components/aggs/top_hit.js | 37 +++++-- .../components/lib/tick_formatter.js | 2 +- .../lib/vis_data/helpers/bucket_transform.js | 2 +- .../lib/vis_data/helpers/get_agg_value.js | 4 +- .../vis_data/helpers/get_agg_value.test.js | 6 +- test/functional/apps/visualize/_tsvb_chart.ts | 103 +++++++++++++++--- .../fixtures/kbn_archiver/visualize.json | 3 +- .../page_objects/visual_builder_page.ts | 47 ++++++++ 8 files changed, 168 insertions(+), 36 deletions(-) diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js b/src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js index 546c09cdf34fd0..b9ef2d89135741 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React from 'react'; +import React, { useMemo, useEffect } from 'react'; import { AggRow } from './agg_row'; import { AggSelect } from './agg_select'; import { FieldSelect } from './field_select'; @@ -62,6 +62,7 @@ const getAggWithOptions = (field = {}, fieldTypesRestriction) => { }, ]; case KBN_FIELD_TYPES.STRING: + case KBN_FIELD_TYPES.DATE: return [ { label: i18n.translate('visTypeTimeseries.topHit.aggWithOptions.concatenate', { @@ -91,16 +92,18 @@ const getOrderOptions = () => [ }, ]; +const AGG_WITH_KEY = 'agg_with'; const ORDER_DATE_RESTRICT_FIELDS = [KBN_FIELD_TYPES.DATE]; +const getModelDefaults = () => ({ + size: 1, + order: 'desc', + [AGG_WITH_KEY]: 'noop', +}); + const TopHitAggUi = (props) => { const { fields, series, panel } = props; - const defaults = { - size: 1, - agg_with: 'noop', - order: 'desc', - }; - const model = { ...defaults, ...props.model }; + const model = useMemo(() => ({ ...getModelDefaults(), ...props.model }), [props.model]); const indexPattern = series.override_index_pattern ? series.series_index_pattern : panel.index_pattern; @@ -110,7 +113,7 @@ const TopHitAggUi = (props) => { PANEL_TYPES.METRIC, PANEL_TYPES.MARKDOWN, ].includes(panel.type) - ? [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.STRING] + ? [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.STRING, KBN_FIELD_TYPES.DATE] : [KBN_FIELD_TYPES.NUMBER]; const handleChange = createChangeHandler(props.onChange, model); @@ -124,13 +127,23 @@ const TopHitAggUi = (props) => { const htmlId = htmlIdGenerator(); const selectedAggWithOption = aggWithOptions.find((option) => { - return model.agg_with === option.value; + return model[AGG_WITH_KEY] === option.value; }); const selectedOrderOption = orderOptions.find((option) => { return model.order === option.value; }); + useEffect(() => { + const defaultFn = aggWithOptions?.[0]?.value; + const aggWith = model[AGG_WITH_KEY]; + if (aggWith && defaultFn && aggWith !== defaultFn && !selectedAggWithOption) { + handleChange({ + [AGG_WITH_KEY]: defaultFn, + }); + } + }, [model, selectedAggWithOption, aggWithOptions, handleChange]); + return ( {
{ )} options={aggWithOptions} selectedOptions={selectedAggWithOption ? [selectedAggWithOption] : []} - onChange={handleSelectChange('agg_with')} + onChange={handleSelectChange(AGG_WITH_KEY)} singleSelection={{ asPlainText: true }} + data-test-subj="topHitAggregateWithComboBox" /> @@ -231,6 +245,7 @@ const TopHitAggUi = (props) => { onChange={handleSelectChange('order_by')} indexPattern={indexPattern} fields={fields} + data-test-subj="topHitOrderByFieldSelect" /> diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.js b/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.js index c1d82a182e509b..9bccc13d192692 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.js +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.js @@ -16,7 +16,7 @@ export const createTickFormatter = (format = '0,0.[00]', template, getConfig = n const fieldFormats = getFieldFormats(); if (!template) template = '{{value}}'; - const render = handlebars.compile(template, { knownHelpersOnly: true }); + const render = handlebars.compile(template, { noEscape: true, knownHelpersOnly: true }); let formatter; if (isDuration(format)) { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js index 16e7b9d6072cba..13b890189325ce 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js @@ -111,7 +111,7 @@ export const bucketTransform = { docs: { top_hits: { size: bucket.size, - _source: { includes: [bucket.field] }, + fields: [bucket.field], }, }, }, diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.js index 32d17ef6d6cb73..90df3f26759599 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.js @@ -45,10 +45,10 @@ export const getAggValue = (row, metric) => { } const hits = get(row, [metric.id, 'docs', 'hits', 'hits'], []); - const values = hits.map((doc) => get(doc, `_source.${metric.field}`)); + const values = hits.map((doc) => doc.fields[metric.field]); const aggWith = (metric.agg_with && aggFns[metric.agg_with]) || aggFns.noop; - return aggWith(values); + return aggWith(values.flat()); case METRIC_TYPES.COUNT: return get(row, 'doc_count', null); default: diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.test.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.test.js index a23c57f5675633..ecbdd1563c3043 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.test.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.test.js @@ -67,11 +67,7 @@ describe('getAggValue', () => { doc_count: 1, docs: { hits: { - hits: [ - { _source: { example: { value: 25 } } }, - { _source: { example: { value: 25 } } }, - { _source: { example: { value: 25 } } }, - ], + hits: [{ fields: { 'example.value': [25, 25, 25] } }], }, }, }, diff --git a/test/functional/apps/visualize/_tsvb_chart.ts b/test/functional/apps/visualize/_tsvb_chart.ts index ca310493960f53..49b2ad8f9646a3 100644 --- a/test/functional/apps/visualize/_tsvb_chart.ts +++ b/test/functional/apps/visualize/_tsvb_chart.ts @@ -24,6 +24,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'timePicker', 'visChart', 'common', + 'settings', ]); describe('visual builder', function describeIndexTests() { @@ -44,14 +45,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); describe('metric', () => { + const { visualBuilder } = PageObjects; + beforeEach(async () => { - await PageObjects.visualBuilder.resetPage(); - await PageObjects.visualBuilder.clickMetric(); - await PageObjects.visualBuilder.checkMetricTabIsPresent(); - await PageObjects.visualBuilder.clickPanelOptions('metric'); - await PageObjects.visualBuilder.setMetricsDataTimerangeMode('Last value'); - await PageObjects.visualBuilder.setDropLastBucket(true); - await PageObjects.visualBuilder.clickDataTab('metric'); + await visualBuilder.resetPage(); + await visualBuilder.clickMetric(); + await visualBuilder.checkMetricTabIsPresent(); + await visualBuilder.clickPanelOptions('metric'); + await visualBuilder.setMetricsDataTimerangeMode('Last value'); + await visualBuilder.setDropLastBucket(true); + await visualBuilder.clickDataTab('metric'); }); it('should not have inspector enabled', async () => { @@ -59,28 +62,98 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should show correct data', async () => { - const value = await PageObjects.visualBuilder.getMetricValue(); + const value = await visualBuilder.getMetricValue(); expect(value).to.eql('156'); }); it('should show correct data with Math Aggregation', async () => { - await PageObjects.visualBuilder.createNewAgg(); - await PageObjects.visualBuilder.selectAggType('math', 1); - await PageObjects.visualBuilder.fillInVariable(); - await PageObjects.visualBuilder.fillInExpression('params.test + 1'); - const value = await PageObjects.visualBuilder.getMetricValue(); + await visualBuilder.createNewAgg(); + await visualBuilder.selectAggType('math', 1); + await visualBuilder.fillInVariable(); + await visualBuilder.fillInExpression('params.test + 1'); + const value = await visualBuilder.getMetricValue(); expect(value).to.eql('157'); }); it('should populate fields for basic functions', async () => { - const { visualBuilder } = PageObjects; - await visualBuilder.selectAggType('Average'); await visualBuilder.setFieldForAggregation('machine.ram'); const isFieldForAggregationValid = await visualBuilder.checkFieldForAggregationValidity(); expect(isFieldForAggregationValid).to.be(true); }); + + it('should show correct data for Value Count with Entire time range mode', async () => { + await visualBuilder.selectAggType('Value Count'); + await visualBuilder.setFieldForAggregation('machine.ram'); + + await visualBuilder.clickPanelOptions('metric'); + await visualBuilder.setMetricsDataTimerangeMode('Entire time range'); + + const value = await visualBuilder.getMetricValue(); + expect(value).to.eql('13,492'); + }); + + it('should show same data for kibana and string index pattern modes', async () => { + await visualBuilder.selectAggType('Max'); + await visualBuilder.setFieldForAggregation('machine.ram'); + const kibanaIndexPatternModeValue = await visualBuilder.getMetricValue(); + + await visualBuilder.clickPanelOptions('metric'); + await visualBuilder.switchIndexPatternSelectionMode(false); + const stringIndexPatternModeValue = await visualBuilder.getMetricValue(); + + expect(kibanaIndexPatternModeValue).to.eql(stringIndexPatternModeValue); + expect(kibanaIndexPatternModeValue).to.eql('32,212,254,720'); + }); + + describe('Color rules', () => { + beforeEach(async () => { + await visualBuilder.selectAggType('Min'); + await visualBuilder.setFieldForAggregation('machine.ram'); + + await visualBuilder.clickPanelOptions('metric'); + await visualBuilder.setColorRuleOperator('>= greater than or equal'); + await visualBuilder.setColorRuleValue(0); + }); + + it('should apply color rules to visualization background', async () => { + await visualBuilder.setColorPickerValue('#FFCFDF'); + + const backGroundStyle = await visualBuilder.getBackgroundStyle(); + expect(backGroundStyle).to.eql('background-color: rgb(255, 207, 223);'); + }); + + it('should apply color rules to metric value', async () => { + await visualBuilder.setColorPickerValue('#AD7DE6', 1); + + const backGroundStyle = await visualBuilder.getMetricValueStyle(); + expect(backGroundStyle).to.eql('color: rgb(173, 125, 230);'); + }); + }); + + describe('Top Hit aggregation', () => { + beforeEach(async () => { + await visualBuilder.selectAggType('Top Hit'); + await visualBuilder.setTopHitOrderByField('@timestamp'); + }); + + it('should show correct data for string type field', async () => { + await visualBuilder.setFieldForAggregation('machine.os.raw'); + await visualBuilder.setTopHitAggregateWithOption('Concatenate'); + + const value = await visualBuilder.getMetricValue(); + expect(value).to.eql('win 7'); + }); + + it('should show correct data for runtime field', async () => { + await visualBuilder.setFieldForAggregation('hello_world_runtime_field'); + await visualBuilder.setTopHitAggregateWithOption('Concatenate'); + + const value = await visualBuilder.getMetricValue(); + expect(value).to.eql('hello world'); + }); + }); }); describe('gauge', () => { diff --git a/test/functional/fixtures/kbn_archiver/visualize.json b/test/functional/fixtures/kbn_archiver/visualize.json index 660da856964b44..225dc0592e87d5 100644 --- a/test/functional/fixtures/kbn_archiver/visualize.json +++ b/test/functional/fixtures/kbn_archiver/visualize.json @@ -3,6 +3,7 @@ "fieldAttrs": "{\"utc_time\":{\"customLabel\":\"UTC time\"}}", "fieldFormatMap": "{\"bytes\":{\"id\":\"bytes\"}}", "fields": "[{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false}]", + "runtimeFieldMap":"{\"hello_world_runtime_field\":{\"type\":\"keyword\",\"script\":{\"source\":\"emit('hello world')\"}}}", "timeFieldName": "@timestamp", "title": "logstash-*" }, @@ -301,4 +302,4 @@ "references": [], "type": "index-pattern", "version": "WzE1LDFd" -} \ No newline at end of file +} diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index 8e28ffab6c9c3d..fd89a88658b3a6 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -575,6 +575,42 @@ export class VisualBuilderPageObject extends FtrService { await this.testSubjects.existOrFail('euiColorPickerPopover', { timeout: 5000 }); } + public async setColorPickerValue(colorHex: string, nth: number = 0): Promise { + const picker = await this.find.allByCssSelector('.tvbColorPicker button'); + await picker[nth].clickMouseButton(); + await this.checkColorPickerPopUpIsPresent(); + await this.find.setValue('.euiColorPicker input', colorHex); + await this.visChart.waitForVisualizationRenderingStabilized(); + } + + public async setColorRuleOperator(condition: string): Promise { + await this.retry.try(async () => { + await this.comboBox.clearInputField('colorRuleOperator'); + await this.comboBox.set('colorRuleOperator', condition); + }); + } + + public async setColorRuleValue(value: number): Promise { + await this.retry.try(async () => { + const colorRuleValueInput = await this.find.byCssSelector( + '[data-test-subj="colorRuleValue"]' + ); + await colorRuleValueInput.type(value.toString()); + }); + } + + public async getBackgroundStyle(): Promise { + await this.visChart.waitForVisualizationRenderingStabilized(); + const visualization = await this.find.byClassName('tvbVis'); + return await visualization.getAttribute('style'); + } + + public async getMetricValueStyle(): Promise { + await this.visChart.waitForVisualizationRenderingStabilized(); + const metricValue = await this.find.byCssSelector('[data-test-subj="tsvbMetricValue"]'); + return await metricValue.getAttribute('style'); + } + public async changePanelPreview(nth: number = 0): Promise { const prevRenderingCount = await this.visChart.getVisualizationRenderingCount(); const changePreviewBtnArray = await this.testSubjects.findAll('AddActivatePanelBtn'); @@ -680,4 +716,15 @@ export class VisualBuilderPageObject extends FtrService { const dataTimeRangeMode = await this.testSubjects.find('dataTimeRangeMode'); return await this.comboBox.isOptionSelected(dataTimeRangeMode, value); } + + public async setTopHitAggregateWithOption(option: string): Promise { + await this.comboBox.set('topHitAggregateWithComboBox', option); + } + + public async setTopHitOrderByField(timeField: string) { + await this.retry.try(async () => { + await this.comboBox.clearInputField('topHitOrderByFieldSelect'); + await this.comboBox.set('topHitOrderByFieldSelect', timeField); + }); + } } From a80791aa4ccc2902820591299ce8a89c364d2cd6 Mon Sep 17 00:00:00 2001 From: Jason Stoltzfus Date: Mon, 12 Jul 2021 10:26:41 -0400 Subject: [PATCH 06/40] Pass locale to calendar (#105134) --- .../components/analytics/components/analytics_filters.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_filters.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_filters.tsx index 0c8455e986ae1d..dd99d368a0105d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_filters.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_filters.tsx @@ -80,6 +80,7 @@ export const AnalyticsFilters: React.FC = () => { 'xpack.enterpriseSearch.appSearch.engine.analytics.filters.startDateAriaLabel', { defaultMessage: 'Filter by start date' } )} + locale={i18n.getLocale()} /> } endDateControl={ @@ -93,6 +94,7 @@ export const AnalyticsFilters: React.FC = () => { 'xpack.enterpriseSearch.appSearch.engine.analytics.filters.endDateAriaLabel', { defaultMessage: 'Filter by end date' } )} + locale={i18n.getLocale()} /> } fullWidth From d6a36926008b000d006d012edafed5cad6a7f3f6 Mon Sep 17 00:00:00 2001 From: Kaarina Tungseth Date: Mon, 12 Jul 2021 09:58:39 -0500 Subject: [PATCH 07/40] [DOCS] Adds how to create dashboard drilldowns for Top N and Table TSVB panels (#104548) --- docs/user/dashboard/tsvb.asciidoc | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/user/dashboard/tsvb.asciidoc b/docs/user/dashboard/tsvb.asciidoc index 89da3f7285924e..11fe71b7639bb5 100644 --- a/docs/user/dashboard/tsvb.asciidoc +++ b/docs/user/dashboard/tsvb.asciidoc @@ -148,6 +148,27 @@ The *Markdown* visualization supports Markdown with Handlebar (mustache) syntax For answers to frequently asked *TSVB* question, review the following. +[float] +===== How do I create dashboard drilldowns for Top N and Table visualizations? + +You can create dashboard drilldowns that include the specified time range for *Top N* and *Table* visualizations. + +. Open the dashboard that you want to link to, then copy the URL. + +. Open the dashboard with the *Top N* and *Table* visualization panel, then click *Edit* in the toolbar. + +. Open the *Top N* or *Table* panel menu, then select *Edit visualization*. + +. Click *Panel options*. + +. In the *Item URL* field, enter the URL. ++ +For example `dashboards#/view/f193ca90-c9f4-11eb-b038-dd3270053a27`. + +. Click *Save and return*. + +. In the toolbar, cick *Save as*, then make sure *Store time with dashboard* is deselected. + [float] ===== Why is my TSVB visualization missing data? From 1aa9459ba2d8c4e736fdae5c0f4f4a59f09c7973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ece=20=C3=96zalp?= Date: Mon, 12 Jul 2021 11:07:34 -0400 Subject: [PATCH 08/40] [CTI] converts disabled panel danger to warning (#104989) --- .../overview_cti_links/cti_disabled_module.tsx | 4 ++-- .../overview_cti_links/cti_inner_panel.tsx | 12 ++++-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx index 21a4beca72f3b2..1600356882c36b 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_disabled_module.tsx @@ -21,11 +21,11 @@ export const CtiDisabledModuleComponent = () => { const danger = useMemo( () => ( + {i18n.DANGER_BUTTON} } diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_inner_panel.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_inner_panel.tsx index 08bf0a432f9bb8..ddff78608dfb02 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_inner_panel.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_cti_links/cti_inner_panel.tsx @@ -17,13 +17,9 @@ const ButtonContainer = styled(EuiFlexGroup)` padding: ${({ theme }) => theme.eui.paddingSizes.s}; `; -const Title = styled(EuiText)<{ textcolor: 'primary' | 'warning' | 'danger' }>` +const Title = styled(EuiText)<{ textcolor: 'primary' | 'warning' }>` color: ${({ theme, textcolor }) => - textcolor === 'primary' - ? theme.eui.euiColorPrimary - : textcolor === 'warning' - ? theme.eui.euiColorWarningText - : theme.eui.euiColorDangerText}; + textcolor === 'primary' ? theme.eui.euiColorPrimary : theme.eui.euiColorWarningText}; margin-bottom: ${({ theme }) => theme.eui.paddingSizes.m}; `; @@ -40,12 +36,12 @@ export const CtiInnerPanel = ({ body, button, }: { - color: 'primary' | 'warning' | 'danger'; + color: 'primary' | 'warning'; title: string; body: string; button?: JSX.Element; }) => { - const iconType = color === 'primary' ? 'iInCircle' : color === 'warning' ? 'help' : 'alert'; + const iconType = color === 'primary' ? 'iInCircle' : 'help'; return ( From 0c9777c6020501b6c953b4bca97f41dd858549f1 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Mon, 12 Jul 2021 11:23:57 -0400 Subject: [PATCH 09/40] [Uptime] Refactor page headers to avoid invalid markup (#104215) * Refactor monitor and waterfall page headers to avoid rendering invalid markup. * Update tests. * Translate page titles. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/monitor/monitor_title.test.tsx | 37 ++++++++++------- .../components/monitor/monitor_title.tsx | 41 +++++++++---------- .../step_detail/step_detail_container.tsx | 9 ++-- .../step_detail/step_page_title.tsx | 13 ++---- x-pack/plugins/uptime/public/routes.tsx | 33 +++++++++++---- .../uptime/server/lib/requests/get_certs.ts | 1 - 6 files changed, 77 insertions(+), 57 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_title.test.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_title.test.tsx index 4fd6335c3d3ca9..726ad235f7f49e 100644 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_title.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/monitor_title.test.tsx @@ -11,7 +11,7 @@ import { screen } from '@testing-library/react'; import { render } from '../../lib/helper/rtl_helpers'; import * as reactRouterDom from 'react-router-dom'; import { Ping } from '../../../common/runtime_types'; -import { MonitorPageTitle } from './monitor_title'; +import { MonitorPageTitle, MonitorPageTitleContent } from './monitor_title'; jest.mock('react-router-dom', () => { const originalModule = jest.requireActual('react-router-dom'); @@ -77,11 +77,17 @@ describe('MonitorTitle component', () => { }); it('renders the monitor heading and EnableMonitorAlert toggle', () => { - render(, { - state: { monitorStatus: { status: monitorStatusWithName, loading: false } }, - }); - expect(screen.getByRole('heading', { level: 1, name: monitorName })).toBeInTheDocument(); - expect(screen.getByTestId('uptimeDisplayDefineConnector')).toBeInTheDocument(); + render( + <> + + + , + { + state: { monitorStatus: { status: monitorStatusWithName, loading: false } }, + } + ); + expect(screen.getByText(monitorName)); + expect(screen.getByRole('switch')).toBeInTheDocument(); }); it('renders the user provided monitorId when the name is not present', () => { @@ -89,21 +95,24 @@ describe('MonitorTitle component', () => { render(, { state: { monitorStatus: { status: defaultMonitorStatus, loading: false } }, }); - expect(screen.getByRole('heading', { level: 1, name: defaultMonitorId })).toBeInTheDocument(); + expect(screen.getByText(defaultMonitorId)); }); it('renders the url when the monitorId is auto generated and the monitor name is not present', () => { mockReactRouterDomHooks({ useParamsResponse: { monitorId: autoGeneratedMonitorIdEncoded } }); - render(, { - state: { monitorStatus: { status: defaultMonitorStatus, loading: false } }, - }); - expect( - screen.getByRole('heading', { level: 1, name: defaultMonitorStatus.url?.full }) - ).toBeInTheDocument(); + render( +
+ +
, + { + state: { monitorStatus: { status: defaultMonitorStatus, loading: false } }, + } + ); + expect(screen.getByText(defaultMonitorStatus!.url!.full!)); }); it('renders beta disclaimer for synthetics monitors', () => { - render(, { + render(, { state: { monitorStatus: { status: defaultBrowserMonitorStatus, loading: false } }, }); const betaLink = screen.getByRole('link', { diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_title.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_title.tsx index 2112af06536695..aa68e2aa7fc4bc 100644 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_title.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/monitor_title.tsx @@ -5,15 +5,7 @@ * 2.0. */ -import { - EuiBadge, - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, - EuiTitle, - EuiLink, - EuiText, -} from '@elastic/eui'; +import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiLink, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; import { useSelector } from 'react-redux'; @@ -40,18 +32,11 @@ const getPageTitle = (monitorId: string, selectedMonitor: Ping | null) => { return monitorId; }; -export const MonitorPageTitle: React.FC = () => { +export const MonitorPageTitleContent: React.FC = () => { const monitorId = useMonitorId(); - const selectedMonitor = useSelector(monitorStatusSelector); - - const nameOrId = selectedMonitor?.monitor?.name || getPageTitle(monitorId, selectedMonitor); - const type = selectedMonitor?.monitor?.type; const isBrowser = type === 'browser'; - - useBreadcrumbs([{ text: nameOrId }]); - const renderMonitorType = (monitorType: string) => { switch (monitorType) { case 'http': @@ -86,12 +71,13 @@ export const MonitorPageTitle: React.FC = () => { return ''; } }; - return ( <> - -

{nameOrId}

-
+ + + + + @@ -118,7 +104,18 @@ export const MonitorPageTitle: React.FC = () => { )} - ); }; + +export const MonitorPageTitle: React.FC = () => { + const monitorId = useMonitorId(); + + const selectedMonitor = useSelector(monitorStatusSelector); + + const nameOrId = selectedMonitor?.monitor?.name || getPageTitle(monitorId, selectedMonitor); + + useBreadcrumbs([{ text: nameOrId }]); + + return {nameOrId}; +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx index 610107f406306a..c24ecd91838659 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx @@ -16,7 +16,7 @@ import { useUiSetting$ } from '../../../../../../../../src/plugins/kibana_react/ import { useMonitorBreadcrumb } from './use_monitor_breadcrumb'; import { ClientPluginsStart } from '../../../../apps/plugin'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; -import { StepPageTitle } from './step_page_title'; +import { StepPageTitleContent } from './step_page_title'; import { StepPageNavigation } from './step_page_nav'; import { WaterfallChartContainer } from './waterfall/waterfall_chart_container'; @@ -78,10 +78,11 @@ export const StepDetailContainer: React.FC = ({ checkGroup, stepIndex }) return ( void; handleNextStep: () => void; } -export const StepPageTitle = ({ - stepName, + +export const StepPageTitleContent = ({ stepIndex, totalSteps, handleNextStep, @@ -29,11 +29,6 @@ export const StepPageTitle = ({ }: Props) => { return ( - - -

{stepName}

-
-
diff --git a/x-pack/plugins/uptime/public/routes.tsx b/x-pack/plugins/uptime/public/routes.tsx index e3c558cee2c320..2b0cc4dc5e5c2b 100644 --- a/x-pack/plugins/uptime/public/routes.tsx +++ b/x-pack/plugins/uptime/public/routes.tsx @@ -23,7 +23,7 @@ import { UptimePage, useUptimeTelemetry } from './hooks'; import { OverviewPageComponent } from './pages/overview'; import { SyntheticsCheckSteps } from './pages/synthetics/synthetics_checks'; import { ClientPluginsStart } from './apps/plugin'; -import { MonitorPageTitle } from './components/monitor/monitor_title'; +import { MonitorPageTitle, MonitorPageTitleContent } from './components/monitor/monitor_title'; import { UptimeDatePicker } from './components/common/uptime_date_picker'; import { useKibana } from '../../../../src/plugins/kibana_react/public'; import { CertRefreshBtn } from './components/certificates/cert_refresh_btn'; @@ -36,10 +36,16 @@ interface RouteProps { dataTestSubj: string; title: string; telemetryId: UptimePage; - pageHeader?: { pageTitle: string | JSX.Element; rightSideItems?: JSX.Element[] }; + pageHeader?: { + children?: JSX.Element; + pageTitle: string | JSX.Element; + rightSideItems?: JSX.Element[]; + }; } -const baseTitle = 'Uptime - Kibana'; +const baseTitle = i18n.translate('xpack.uptime.routes.baseTitle', { + defaultMessage: 'Uptime - Kibana', +}); export const MONITORING_OVERVIEW_LABEL = i18n.translate('xpack.uptime.overview.heading', { defaultMessage: 'Monitors', @@ -47,18 +53,25 @@ export const MONITORING_OVERVIEW_LABEL = i18n.translate('xpack.uptime.overview.h const Routes: RouteProps[] = [ { - title: `Monitor | ${baseTitle}`, + title: i18n.translate('xpack.uptime.monitorRoute.title', { + defaultMessage: 'Monitor | {baseTitle}', + values: { baseTitle }, + }), path: MONITOR_ROUTE, component: MonitorPage, dataTestSubj: 'uptimeMonitorPage', telemetryId: UptimePage.Monitor, pageHeader: { + children: , pageTitle: , rightSideItems: [], }, }, { - title: `Settings | ${baseTitle}`, + title: i18n.translate('xpack.uptime.settingsRoute.title', { + defaultMessage: `Settings | {baseTitle}`, + values: { baseTitle }, + }), path: SETTINGS_ROUTE, component: SettingsPage, dataTestSubj: 'uptimeSettingsPage', @@ -70,7 +83,10 @@ const Routes: RouteProps[] = [ }, }, { - title: `Certificates | ${baseTitle}`, + title: i18n.translate('xpack.uptime.certificatesRoute.title', { + defaultMessage: `Certificates | {baseTitle}`, + values: { baseTitle }, + }), path: CERTIFICATES_ROUTE, component: CertificatesPage, dataTestSubj: 'uptimeCertificatesPage', @@ -81,7 +97,10 @@ const Routes: RouteProps[] = [ }, }, { - title: baseTitle, + title: i18n.translate('xpack.uptime.stepDetailRoute.title', { + defaultMessage: 'Synthetics detail | {baseTitle}', + values: { baseTitle }, + }), path: STEP_DETAIL_ROUTE, component: StepDetailPage, dataTestSubj: 'uptimeStepDetailPage', diff --git a/x-pack/plugins/uptime/server/lib/requests/get_certs.ts b/x-pack/plugins/uptime/server/lib/requests/get_certs.ts index 1b20ed9085fefc..7639484f517374 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_certs.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_certs.ts @@ -138,7 +138,6 @@ export const getCerts: UMElasticsearchQueryFn = asyn searchBody.query.bool.filter.push(validityFilters); } - // console.log(JSON.stringify(params, null, 2)); const { body: result } = await uptimeEsClient.search({ body: searchBody, }); From 3e5ed774700336657f25e8f0aef10e3bf17a93a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?= Date: Mon, 12 Jul 2021 18:55:06 +0300 Subject: [PATCH 10/40] [Osquery] Fix 7.14 live query history view (#105211) --- .../action_results/action_results_summary.tsx | 22 +------ .../osquery/public/actions/actions_table.tsx | 19 ++++++ .../public/agents/agent_id_to_name.tsx | 37 ++++++++++++ .../osquery/public/agents/agents_table.tsx | 24 ++++++-- .../public/agents/use_agent_details.ts | 36 +++++++++++ .../osquery/public/agents/use_all_agents.ts | 2 +- .../public/live_queries/form/index.tsx | 2 +- .../public/routes/live_queries/new/index.tsx | 10 +++- .../routes/saved_queries/list/index.tsx | 60 ++++++++++--------- .../saved_queries/saved_queries_dropdown.tsx | 41 +++++++++---- .../scheduled_query_group_queries_table.tsx | 2 +- .../actions/all/query.all_actions.dsl.ts | 23 +++++-- 12 files changed, 207 insertions(+), 71 deletions(-) create mode 100644 x-pack/plugins/osquery/public/agents/agent_id_to_name.tsx create mode 100644 x-pack/plugins/osquery/public/agents/use_agent_details.ts diff --git a/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx b/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx index d3b0e38a5e033b..bf4c97d63d74ca 100644 --- a/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx +++ b/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx @@ -8,15 +8,13 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { i18n } from '@kbn/i18n'; -import { EuiLink, EuiInMemoryTable, EuiCodeBlock } from '@elastic/eui'; +import { EuiInMemoryTable, EuiCodeBlock } from '@elastic/eui'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { PLUGIN_ID } from '../../../fleet/common'; -import { pagePathGetters } from '../../../fleet/public'; +import { AgentIdToName } from '../agents/agent_id_to_name'; import { useActionResults } from './use_action_results'; import { useAllResults } from '../results/use_all_results'; import { Direction } from '../../common/search_strategy'; -import { useKibana } from '../common/lib/kibana'; interface ActionResultsSummaryProps { actionId: string; @@ -35,7 +33,6 @@ const ActionResultsSummaryComponent: React.FC = ({ expirationDate, agentIds, }) => { - const getUrlForApp = useKibana().services.application.getUrlForApp; // @ts-expect-error update types const [pageIndex, setPageIndex] = useState(0); // @ts-expect-error update types @@ -70,20 +67,7 @@ const ActionResultsSummaryComponent: React.FC = ({ isLive, }); - const renderAgentIdColumn = useCallback( - (agentId) => ( - - {agentId} - - ), - [getUrlForApp] - ); + const renderAgentIdColumn = useCallback((agentId) => , []); const renderRowsColumn = useCallback( (_, item) => { diff --git a/x-pack/plugins/osquery/public/actions/actions_table.tsx b/x-pack/plugins/osquery/public/actions/actions_table.tsx index 0ee928ad8aa147..045c1f67b070da 100644 --- a/x-pack/plugins/osquery/public/actions/actions_table.tsx +++ b/x-pack/plugins/osquery/public/actions/actions_table.tsx @@ -9,6 +9,7 @@ import { isArray } from 'lodash'; import { i18n } from '@kbn/i18n'; import { EuiBasicTable, EuiButtonIcon, EuiCodeBlock, formatDate } from '@elastic/eui'; import React, { useState, useCallback, useMemo } from 'react'; +import { useHistory } from 'react-router-dom'; import { useAllActions } from './use_all_actions'; import { Direction } from '../../common/search_strategy'; @@ -27,6 +28,7 @@ const ActionTableResultsButton = React.memo(({ ac ActionTableResultsButton.displayName = 'ActionTableResultsButton'; const ActionsTableComponent = () => { + const { push } = useHistory(); const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(20); @@ -67,6 +69,16 @@ const ActionsTableComponent = () => { [] ); + const handlePlayClick = useCallback( + (item) => + push('/live_queries/new', { + form: { + query: item._source?.data?.query, + }, + }), + [push] + ); + const columns = useMemo( () => [ { @@ -106,6 +118,11 @@ const ActionsTableComponent = () => { defaultMessage: 'View details', }), actions: [ + { + type: 'icon', + icon: 'play', + onClick: handlePlayClick, + }, { render: renderActionsColumn, }, @@ -113,6 +130,7 @@ const ActionsTableComponent = () => { }, ], [ + handlePlayClick, renderActionsColumn, renderAgentsColumn, renderCreatedByColumn, @@ -135,6 +153,7 @@ const ActionsTableComponent = () => { = ({ agentId }) => { + const getUrlForApp = useKibana().services.application.getUrlForApp; + const { data } = useAgentDetails({ agentId }); + + return ( + + {data?.item.local_metadata.host.name ?? agentId} + + ); +}; + +export const AgentIdToName = React.memo(AgentIdToNameComponent); diff --git a/x-pack/plugins/osquery/public/agents/agents_table.tsx b/x-pack/plugins/osquery/public/agents/agents_table.tsx index 7e8f49c051614d..53e2ce1d534204 100644 --- a/x-pack/plugins/osquery/public/agents/agents_table.tsx +++ b/x-pack/plugins/osquery/public/agents/agents_table.tsx @@ -21,7 +21,12 @@ import { generateAgentSelection, } from './helpers'; -import { SELECT_AGENT_LABEL, generateSelectedAgentsMessage } from './translations'; +import { + SELECT_AGENT_LABEL, + generateSelectedAgentsMessage, + ALL_AGENTS_LABEL, + AGENT_POLICY_LABEL, +} from './translations'; import { AGENT_GROUP_KEY, @@ -72,8 +77,17 @@ const AgentsTableComponent: React.FC = ({ agentSelection, onCh useEffect(() => { if (agentSelection && !defaultValueInitialized.current && options.length) { - if (agentSelection.policiesSelected) { - const policyOptions = find(['label', 'Policy'], options); + if (agentSelection.allAgentsSelected) { + const allAgentsOptions = find(['label', ALL_AGENTS_LABEL], options); + + if (allAgentsOptions?.options) { + setSelectedOptions(allAgentsOptions.options); + defaultValueInitialized.current = true; + } + } + + if (agentSelection.policiesSelected.length) { + const policyOptions = find(['label', AGENT_POLICY_LABEL], options); if (policyOptions) { const defaultOptions = policyOptions.options?.filter((option) => @@ -82,12 +96,12 @@ const AgentsTableComponent: React.FC = ({ agentSelection, onCh if (defaultOptions?.length) { setSelectedOptions(defaultOptions); + defaultValueInitialized.current = true; } - defaultValueInitialized.current = true; } } } - }, [agentSelection, options]); + }, [agentSelection, options, selectedOptions]); useEffect(() => { // update the groups when groups or agents have changed diff --git a/x-pack/plugins/osquery/public/agents/use_agent_details.ts b/x-pack/plugins/osquery/public/agents/use_agent_details.ts new file mode 100644 index 00000000000000..1a0663812dec36 --- /dev/null +++ b/x-pack/plugins/osquery/public/agents/use_agent_details.ts @@ -0,0 +1,36 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { useQuery } from 'react-query'; + +import { GetOneAgentResponse, agentRouteService } from '../../../fleet/common'; +import { useErrorToast } from '../common/hooks/use_error_toast'; +import { useKibana } from '../common/lib/kibana'; + +interface UseAgentDetails { + agentId: string; +} + +export const useAgentDetails = ({ agentId }: UseAgentDetails) => { + const { http } = useKibana().services; + const setErrorToast = useErrorToast(); + return useQuery( + ['agentDetails', agentId], + () => http.get(agentRouteService.getInfoPath(agentId)), + { + enabled: agentId.length > 0, + onSuccess: () => setErrorToast(), + onError: (error) => + setErrorToast(error as Error, { + title: i18n.translate('xpack.osquery.agentDetails.fetchError', { + defaultMessage: 'Error while fetching agent details', + }), + }), + } + ); +}; diff --git a/x-pack/plugins/osquery/public/agents/use_all_agents.ts b/x-pack/plugins/osquery/public/agents/use_all_agents.ts index 30ba4d2f579079..cda15cc8054371 100644 --- a/x-pack/plugins/osquery/public/agents/use_all_agents.ts +++ b/x-pack/plugins/osquery/public/agents/use_all_agents.ts @@ -38,7 +38,7 @@ export const useAllAgents = ( let kuery = `last_checkin_status: online and (${policyFragment})`; if (searchValue) { - kuery += `and (local_metadata.host.hostname:*${searchValue}* or local_metadata.elastic.agent.id:*${searchValue}*)`; + kuery += ` and (local_metadata.host.hostname:*${searchValue}* or local_metadata.elastic.agent.id:*${searchValue}*)`; } return http.get(agentRouteService.getListPath(), { diff --git a/x-pack/plugins/osquery/public/live_queries/form/index.tsx b/x-pack/plugins/osquery/public/live_queries/form/index.tsx index 8654a74fecfb40..658280042696ef 100644 --- a/x-pack/plugins/osquery/public/live_queries/form/index.tsx +++ b/x-pack/plugins/osquery/public/live_queries/form/index.tsx @@ -110,7 +110,7 @@ const LiveQueryFormComponent: React.FC = ({ { agentSelection: { agents: [], - allAgentsSelected: false, + allAgentsSelected: true, platformsSelected: [], policiesSelected: [], }, diff --git a/x-pack/plugins/osquery/public/routes/live_queries/new/index.tsx b/x-pack/plugins/osquery/public/routes/live_queries/new/index.tsx index 9967eb97cddf22..40614c1f3e1b8e 100644 --- a/x-pack/plugins/osquery/public/routes/live_queries/new/index.tsx +++ b/x-pack/plugins/osquery/public/routes/live_queries/new/index.tsx @@ -8,7 +8,7 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useMemo } from 'react'; -import { useLocation } from 'react-router-dom'; +import { useHistory, useLocation } from 'react-router-dom'; import qs from 'query-string'; import { WithHeaderLayout } from '../../../components/layouts'; @@ -19,12 +19,18 @@ import { BetaBadge, BetaBadgeRowWrapper } from '../../../components/beta_badge'; const NewLiveQueryPageComponent = () => { useBreadcrumbs('live_query_new'); + const { replace } = useHistory(); const location = useLocation(); const liveQueryListProps = useRouterNavigate('live_queries'); const formDefaultValue = useMemo(() => { const queryParams = qs.parse(location.search); + if (location.state?.form.query) { + replace({ state: null }); + return { query: location.state?.form.query }; + } + if (queryParams?.agentPolicyId) { return { agentSelection: { @@ -37,7 +43,7 @@ const NewLiveQueryPageComponent = () => { } return undefined; - }, [location.search]); + }, [location.search, location.state, replace]); const LeftColumn = useMemo( () => ( diff --git a/x-pack/plugins/osquery/public/routes/saved_queries/list/index.tsx b/x-pack/plugins/osquery/public/routes/saved_queries/list/index.tsx index 7e8e8e543dfabf..8738c06d065979 100644 --- a/x-pack/plugins/osquery/public/routes/saved_queries/list/index.tsx +++ b/x-pack/plugins/osquery/public/routes/saved_queries/list/index.tsx @@ -16,6 +16,7 @@ import { import React, { useCallback, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { useHistory } from 'react-router-dom'; import { SavedObject } from 'kibana/public'; import { WithHeaderLayout } from '../../../components/layouts'; @@ -51,6 +52,7 @@ const EditButton = React.memo(EditButtonComponent); const SavedQueriesPageComponent = () => { useBreadcrumbs('saved_queries'); + const { push } = useHistory(); const newQueryLinkProps = useRouterNavigate('saved_queries/new'); const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(10); @@ -59,21 +61,15 @@ const SavedQueriesPageComponent = () => { const { data } = useSavedQueries({ isLive: true }); - // const handlePlayClick = useCallback( - // (item) => - // push({ - // search: qs.stringify({ - // tab: 'live_query', - // }), - // state: { - // query: { - // id: item.id, - // query: item.attributes.query, - // }, - // }, - // }), - // [push] - // ); + const handlePlayClick = useCallback( + (item) => + push('/live_queries/new', { + form: { + savedQueryId: item.id, + }, + }), + [push] + ); const renderEditAction = useCallback( (item: SavedObject<{ name: string }>) => ( @@ -96,45 +92,53 @@ const SavedQueriesPageComponent = () => { () => [ { field: 'attributes.id', - name: 'Query ID', + name: i18n.translate('xpack.osquery.savedQueries.table.queryIdColumnTitle', { + defaultMessage: 'Query ID', + }), sortable: true, truncateText: true, }, { field: 'attributes.description', - name: 'Description', + name: i18n.translate('xpack.osquery.savedQueries.table.descriptionColumnTitle', { + defaultMessage: 'Description', + }), sortable: true, truncateText: true, }, { field: 'attributes.created_by', - name: 'Created by', + name: i18n.translate('xpack.osquery.savedQueries.table.createdByColumnTitle', { + defaultMessage: 'Created by', + }), sortable: true, truncateText: true, }, { field: 'attributes.updated_at', - name: 'Last updated at', + name: i18n.translate('xpack.osquery.savedQueries.table.updatedAtColumnTitle', { + defaultMessage: 'Last updated at', + }), sortable: (item: SavedObject<{ updated_at: string }>) => item.attributes.updated_at ? Date.parse(item.attributes.updated_at) : 0, truncateText: true, render: renderUpdatedAt, }, { - name: 'Actions', + name: i18n.translate('xpack.osquery.savedQueries.table.actionsColumnTitle', { + defaultMessage: 'Actions', + }), actions: [ - // { - // name: 'Live query', - // description: 'Run live query', - // type: 'icon', - // icon: 'play', - // onClick: handlePlayClick, - // }, + { + type: 'icon', + icon: 'play', + onClick: handlePlayClick, + }, { render: renderEditAction }, ], }, ], - [renderEditAction, renderUpdatedAt] + [handlePlayClick, renderEditAction, renderUpdatedAt] ); const onTableChange = useCallback(({ page = {}, sort = {} }) => { diff --git a/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx b/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx index e30954a695b2d9..073b56bfd1d4c8 100644 --- a/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx +++ b/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx @@ -7,10 +7,11 @@ import { find } from 'lodash/fp'; import { EuiCodeBlock, EuiFormRow, EuiComboBox, EuiText } from '@elastic/eui'; -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { SimpleSavedObject } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { useHistory, useLocation } from 'react-router-dom'; import { useSavedQueries } from './use_saved_queries'; @@ -29,19 +30,25 @@ const SavedQueriesDropdownComponent: React.FC = ({ disabled, onChange, }) => { + const { replace } = useHistory(); + const location = useLocation(); const [selectedOptions, setSelectedOptions] = useState([]); const { data } = useSavedQueries({}); - const queryOptions = - data?.savedObjects?.map((savedQuery) => ({ - label: savedQuery.attributes.id ?? '', - value: { - id: savedQuery.attributes.id, - description: savedQuery.attributes.description, - query: savedQuery.attributes.query, - }, - })) ?? []; + const queryOptions = useMemo( + () => + data?.savedObjects?.map((savedQuery) => ({ + label: savedQuery.attributes.id ?? '', + value: { + savedObjectId: savedQuery.id, + id: savedQuery.attributes.id, + description: savedQuery.attributes.description, + query: savedQuery.attributes.query, + }, + })) ?? [], + [data?.savedObjects] + ); const handleSavedQueryChange = useCallback( (newSelectedOptions) => { @@ -73,6 +80,20 @@ const SavedQueriesDropdownComponent: React.FC = ({ [] ); + useEffect(() => { + const savedQueryId = location.state?.form?.savedQueryId; + + if (savedQueryId) { + const savedQueryOption = find(['value.savedObjectId', savedQueryId], queryOptions); + + if (savedQueryOption) { + handleSavedQueryChange([savedQueryOption]); + } + + replace({ state: null }); + } + }, [handleSavedQueryChange, replace, location.state, queryOptions]); + return ( Date: Mon, 12 Jul 2021 11:56:43 -0400 Subject: [PATCH 11/40] [Fleet] Fix enrollment flyout with fleet server from policy page (#104542) * [Fleet] Fix enrollment flyout with fleet server from policy page * Fix tests * Show enrollment instructions for add agent from fleet server policy * Fix conditions to show fleet server setup in flyout Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../agent_enrollment_flyout.test.mocks.ts | 1 + .../agent_enrollment_flyout.test.tsx | 6 ++- .../agent_enrollment_flyout/index.tsx | 39 ++++++++++++-- .../managed_instructions.tsx | 54 ++++++++++++------- .../agent_enrollment_flyout/steps.tsx | 22 ++------ .../agent_enrollment_flyout/types.ts | 8 ++- x-pack/plugins/fleet/public/types/index.ts | 1 + 7 files changed, 87 insertions(+), 44 deletions(-) diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts index d2e7c4089e88bd..5c292187982dc0 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts @@ -11,6 +11,7 @@ jest.mock('../../hooks/use_request', () => { ...module, useGetSettings: jest.fn(), sendGetFleetStatus: jest.fn(), + sendGetOneAgentPolicy: jest.fn(), }; }); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx index f68b1b878c51c0..18296134ee1a71 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx @@ -16,7 +16,7 @@ import { coreMock } from 'src/core/public/mocks'; import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; import type { AgentPolicy } from '../../../common'; -import { useGetSettings, sendGetFleetStatus } from '../../hooks/use_request'; +import { useGetSettings, sendGetFleetStatus, sendGetOneAgentPolicy } from '../../hooks/use_request'; import { FleetStatusProvider, ConfigContext } from '../../hooks'; import { useFleetServerInstructions } from '../../applications/fleet/sections/agents/agent_requirements_page/components'; @@ -79,6 +79,10 @@ describe('', () => { data: { isReady: true }, }); + (sendGetOneAgentPolicy as jest.Mock).mockResolvedValue({ + data: { item: { package_policies: [] } }, + }); + (useFleetServerInstructions as jest.Mock).mockReturnValue({ serviceToken: 'test', getServiceToken: jest.fn(), diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx index 9b82b2a80b5e14..87911e5d6c2c78 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx @@ -22,7 +22,9 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { useGetSettings, useUrlModal } from '../../hooks'; +import { useGetSettings, useUrlModal, sendGetOneAgentPolicy, useFleetStatus } from '../../hooks'; +import { FLEET_SERVER_PACKAGE } from '../../constants'; +import type { PackagePolicy } from '../../types'; import { ManagedInstructions } from './managed_instructions'; import { StandaloneInstructions } from './standalone_instructions'; @@ -63,6 +65,30 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ } }, [modal, lastModal, settings]); + const fleetStatus = useFleetStatus(); + const [policyId, setSelectedPolicyId] = useState(agentPolicy?.id); + const [isFleetServerPolicySelected, setIsFleetServerPolicySelected] = useState(false); + + useEffect(() => { + async function checkPolicyIsFleetServer() { + if (policyId && setIsFleetServerPolicySelected) { + const agentPolicyRequest = await sendGetOneAgentPolicy(policyId); + if ( + agentPolicyRequest.data?.item && + (agentPolicyRequest.data.item.package_policies as PackagePolicy[]).some( + (packagePolicy) => packagePolicy.package?.name === FLEET_SERVER_PACKAGE + ) + ) { + setIsFleetServerPolicySelected(true); + } else { + setIsFleetServerPolicySelected(false); + } + } + } + + checkPolicyIsFleetServer(); + }, [policyId]); + const isLoadingInitialRequest = settings.isLoading && settings.isInitialRequest; return ( @@ -110,16 +136,23 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ ) : undefined } > - {fleetServerHosts.length === 0 && mode === 'managed' ? null : mode === 'managed' ? ( + {mode === 'managed' ? ( ) : ( diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx index 61f86335cd7f93..8054c48fbbaa80 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx @@ -11,7 +11,7 @@ import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/st import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { useGetOneEnrollmentAPIKey, useGetSettings, useLink, useFleetStatus } from '../../hooks'; +import { useGetOneEnrollmentAPIKey, useLink, useFleetStatus } from '../../hooks'; import { ManualInstructions } from '../../components/enrollment_instructions'; import { @@ -56,14 +56,19 @@ const FleetServerMissingRequirements = () => { }; export const ManagedInstructions = React.memo( - ({ agentPolicy, agentPolicies, viewDataStep }) => { + ({ + agentPolicy, + agentPolicies, + viewDataStep, + setSelectedPolicyId, + isFleetServerPolicySelected, + settings, + }) => { const fleetStatus = useFleetStatus(); const [selectedApiKeyId, setSelectedAPIKeyId] = useState(); - const [isFleetServerPolicySelected, setIsFleetServerPolicySelected] = useState(false); const apiKey = useGetOneEnrollmentAPIKey(selectedApiKeyId); - const settings = useGetSettings(); const fleetServerInstructions = useFleetServerInstructions(apiKey?.data?.item?.policy_id); const fleetServerSteps = useMemo(() => { @@ -88,7 +93,7 @@ export const ManagedInstructions = React.memo( }, [fleetServerInstructions]); const steps = useMemo(() => { - const fleetServerHosts = settings.data?.item?.fleet_server_hosts || []; + const fleetServerHosts = settings?.fleet_server_hosts || []; const baseSteps: EuiContainedStepProps[] = [ DownloadStep(), !agentPolicy @@ -96,7 +101,7 @@ export const ManagedInstructions = React.memo( agentPolicies, selectedApiKeyId, setSelectedAPIKeyId, - setIsFleetServerPolicySelected, + setSelectedPolicyId, }) : AgentEnrollmentKeySelectionStep({ agentPolicy, selectedApiKeyId, setSelectedAPIKeyId }), ]; @@ -121,30 +126,39 @@ export const ManagedInstructions = React.memo( }, [ agentPolicy, selectedApiKeyId, + setSelectedPolicyId, setSelectedAPIKeyId, agentPolicies, apiKey.data, fleetServerSteps, isFleetServerPolicySelected, - settings.data?.item?.fleet_server_hosts, + settings?.fleet_server_hosts, viewDataStep, ]); + if (fleetStatus.isReady && settings?.fleet_server_hosts.length === 0) { + return null; + } + + if (fleetStatus.isReady) { + return ( + <> + + + + + + + ); + } + return ( <> - {fleetStatus.isReady ? ( - <> - - - - - - - ) : fleetStatus.missingRequirements?.length === 1 && - fleetStatus.missingRequirements[0] === 'fleet_server' ? ( + {fleetStatus.missingRequirements?.length === 1 && + fleetStatus.missingRequirements[0] === 'fleet_server' ? ( ) : ( diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx index 6cffa39628d923..1cfdc45fb7dba0 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx @@ -11,9 +11,8 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import semver from 'semver'; -import type { AgentPolicy, PackagePolicy } from '../../types'; -import { sendGetOneAgentPolicy, useKibanaVersion } from '../../hooks'; -import { FLEET_SERVER_PACKAGE } from '../../constants'; +import type { AgentPolicy } from '../../types'; +import { useKibanaVersion } from '../../hooks'; import { EnrollmentStepAgentPolicy } from './agent_policy_selection'; import { AdvancedAgentAuthenticationSettings } from './advanced_agent_authentication_settings'; @@ -69,13 +68,11 @@ export const AgentPolicySelectionStep = ({ selectedApiKeyId, setSelectedAPIKeyId, excludeFleetServer, - setIsFleetServerPolicySelected, }: { agentPolicies?: AgentPolicy[]; setSelectedPolicyId?: (policyId?: string) => void; selectedApiKeyId?: string; setSelectedAPIKeyId?: (key?: string) => void; - setIsFleetServerPolicySelected?: (selected: boolean) => void; excludeFleetServer?: boolean; }) => { const regularAgentPolicies = useMemo(() => { @@ -92,21 +89,8 @@ export const AgentPolicySelectionStep = ({ if (setSelectedPolicyId) { setSelectedPolicyId(policyId); } - if (policyId && setIsFleetServerPolicySelected) { - const agentPolicyRequest = await sendGetOneAgentPolicy(policyId); - if ( - agentPolicyRequest.data?.item && - (agentPolicyRequest.data.item.package_policies as PackagePolicy[]).some( - (packagePolicy) => packagePolicy.package?.name === FLEET_SERVER_PACKAGE - ) - ) { - setIsFleetServerPolicySelected(true); - } else { - setIsFleetServerPolicySelected(false); - } - } }, - [setIsFleetServerPolicySelected, setSelectedPolicyId] + [setSelectedPolicyId] ); return { diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts index 9ee514c634655f..282a5b243caed2 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts @@ -7,7 +7,7 @@ import type { EuiStepProps } from '@elastic/eui'; -import type { AgentPolicy } from '../../types'; +import type { AgentPolicy, Settings } from '../../types'; export interface BaseProps { /** @@ -27,4 +27,10 @@ export interface BaseProps { * in some way. This is an area for consumers to render a button and text explaining how data can be viewed. */ viewDataStep?: EuiStepProps; + + settings?: Settings; + + setSelectedPolicyId?: (policyId?: string) => void; + + isFleetServerPolicySelected?: boolean; } diff --git a/x-pack/plugins/fleet/public/types/index.ts b/x-pack/plugins/fleet/public/types/index.ts index f21552d68e77b6..c91ec42d3e5277 100644 --- a/x-pack/plugins/fleet/public/types/index.ts +++ b/x-pack/plugins/fleet/public/types/index.ts @@ -27,6 +27,7 @@ export { PackagePolicyPackage, Output, DataStream, + Settings, // API schema - misc setup, status GetFleetStatusResponse, // API schemas - Agent policy From 42c743be8848e08761f76903c4aa1f0b5f8e899a Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Mon, 12 Jul 2021 12:50:41 -0400 Subject: [PATCH 12/40] [Top Menu] Increase size of top menu links to `s` (#103144) * Increased non-emphasized header links size from `xs` to `s` * [Observability] Updating header links to use EuiHeaderLink * [Spaces Menu] Larger spinner * [Help Menu] Increase size of links * [Canvas] Increase size to `s` --- .../chrome/ui/header/header_help_menu.tsx | 12 +-- .../data/public/ui/filter_bar/filter_bar.tsx | 2 +- .../index_pattern_table.tsx | 2 +- .../public/top_nav_menu/top_nav_menu_item.tsx | 2 +- .../app/RumDashboard/ActionMenu/index.tsx | 60 ++++++-------- .../alerting_popover_flyout.tsx | 1 - .../anomaly_detection_setup_link.tsx | 1 - .../shared/apm_header_action_menu/index.tsx | 2 +- .../function_reference_generator.tsx | 2 +- .../public/components/help_menu/help_menu.tsx | 4 +- .../__snapshots__/edit_menu.stories.storyshot | 12 +-- .../edit_menu/edit_menu.component.tsx | 2 +- .../share_menu.stories.storyshot | 4 +- .../share_menu/share_menu.component.tsx | 2 +- .../__snapshots__/view_menu.stories.storyshot | 8 +- .../view_menu/view_menu.component.tsx | 2 +- .../file_error_callouts.tsx | 2 +- .../components/metrics_alert_dropdown.tsx | 7 +- .../components/alert_dropdown.tsx | 7 +- .../infra/public/pages/logs/page_content.tsx | 37 ++++----- .../infra/public/pages/metrics/index.tsx | 43 ++++------ .../anomaly_detection_flyout.tsx | 7 +- .../nav_control/nav_control_popover.tsx | 2 +- .../common/header/action_menu_content.tsx | 82 +++++++++---------- .../alerts/toggle_alert_flyout_button.tsx | 7 +- .../overview/synthetics_callout.test.tsx | 6 -- .../overview/synthetics_callout.tsx | 10 +-- 27 files changed, 134 insertions(+), 194 deletions(-) diff --git a/src/core/public/chrome/ui/header/header_help_menu.tsx b/src/core/public/chrome/ui/header/header_help_menu.tsx index c6a09c1177a5e8..cbf89bba2ca443 100644 --- a/src/core/public/chrome/ui/header/header_help_menu.tsx +++ b/src/core/public/chrome/ui/header/header_help_menu.tsx @@ -211,7 +211,7 @@ export class HeaderHelpMenu extends Component { return ( - + { - + { - + { @@ -330,7 +330,7 @@ export class HeaderHelpMenu extends Component { {customLinks} {content && ( <> - {customLinks && } + {customLinks && } )} @@ -383,7 +383,7 @@ const createCustomLink = ( ) => { return ( - + {text} {addSpacer && } diff --git a/src/plugins/data/public/ui/filter_bar/filter_bar.tsx b/src/plugins/data/public/ui/filter_bar/filter_bar.tsx index 4655bbf8e91a55..cc796ad749f0bc 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_bar.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_bar.tsx @@ -77,7 +77,7 @@ function FilterBarUI(props: Props) { const button = ( setIsAddFilterPopoverOpen(true)} data-test-subj="addFilter" className="globalFilterBar__addButton" diff --git a/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx b/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx index 6bd06528084ce9..6405a81282471e 100644 --- a/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx +++ b/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx @@ -132,7 +132,7 @@ export const IndexPatternTable = ({ canSave, history }: Props) => { } ) => ( <> - + {name}   diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx index 523bf07f828c95..f4ca53a9a974e4 100644 --- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx +++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx @@ -42,7 +42,7 @@ export function TopNavMenuItem(props: TopNavMenuData) { {upperFirst(props.label || props.id!)} ) : ( - + {upperFirst(props.label || props.id!)} ); diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/ActionMenu/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/ActionMenu/index.tsx index 63ba7047696cac..4e6544a20f3015 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/ActionMenu/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/ActionMenu/index.tsx @@ -6,12 +6,7 @@ */ import React from 'react'; -import { - EuiButtonEmpty, - EuiFlexGroup, - EuiFlexItem, - EuiToolTip, -} from '@elastic/eui'; +import { EuiHeaderLinks, EuiHeaderLink, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { createExploratoryViewUrl, @@ -62,38 +57,29 @@ export function UXActionMenu({ - - - {ANALYZE_MESSAGE}

}> - - {ANALYZE_DATA} - -
-
- - + {ANALYZE_MESSAGE}

}> + - {i18n.translate('xpack.apm.addDataButtonLabel', { - defaultMessage: 'Add data', - })} -
-
-
+ {ANALYZE_DATA} +
+ + + {i18n.translate('xpack.apm.addDataButtonLabel', { + defaultMessage: 'Add data', + })} + + ); } diff --git a/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/alerting_popover_flyout.tsx b/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/alerting_popover_flyout.tsx index ca73f6ddd05b34..4abd36a2773119 100644 --- a/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/alerting_popover_flyout.tsx +++ b/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/alerting_popover_flyout.tsx @@ -66,7 +66,6 @@ export function AlertingPopoverAndFlyout({ const button = ( - + {i18n.translate('xpack.apm.settingsLinkLabel', { defaultMessage: 'Settings', })} diff --git a/x-pack/plugins/canvas/public/components/function_reference_generator/function_reference_generator.tsx b/x-pack/plugins/canvas/public/components/function_reference_generator/function_reference_generator.tsx index 81532816d9c830..eb394801f549c4 100644 --- a/x-pack/plugins/canvas/public/components/function_reference_generator/function_reference_generator.tsx +++ b/x-pack/plugins/canvas/public/components/function_reference_generator/function_reference_generator.tsx @@ -29,7 +29,7 @@ export const FunctionReferenceGenerator: FC = ({ functionRegistry }) => { }; return ( - + Generate function reference ); diff --git a/x-pack/plugins/canvas/public/components/help_menu/help_menu.tsx b/x-pack/plugins/canvas/public/components/help_menu/help_menu.tsx index 2877ccf41056df..af1850beb5290f 100644 --- a/x-pack/plugins/canvas/public/components/help_menu/help_menu.tsx +++ b/x-pack/plugins/canvas/public/components/help_menu/help_menu.tsx @@ -46,13 +46,13 @@ export const HelpMenu: FC = ({ functionRegistry }) => { return ( <> - + {strings.getKeyboardShortcutsLinkLabel()} {FunctionReferenceGenerator ? ( - + ) : null} diff --git a/x-pack/plugins/canvas/public/components/workpad_header/edit_menu/__stories__/__snapshots__/edit_menu.stories.storyshot b/x-pack/plugins/canvas/public/components/workpad_header/edit_menu/__stories__/__snapshots__/edit_menu.stories.storyshot index cc33ae3526c0c3..f2bc9c57cbcc66 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/edit_menu/__stories__/__snapshots__/edit_menu.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/workpad_header/edit_menu/__stories__/__snapshots__/edit_menu.stories.storyshot @@ -9,7 +9,7 @@ exports[`Storyshots components/WorkpadHeader/EditMenu 2 elements selected 1`] = >