From 89d6cffbd03fb311a3991595f4dc0370d55c4cd1 Mon Sep 17 00:00:00 2001
From: Yngrid Coello
Date: Fri, 14 Jul 2023 09:45:31 +0200
Subject: [PATCH 01/50] [Logs onboarding] Landing page style changed (#161542)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Relates to https://github.com/elastic/kibana/issues/159655.
### Changes
This PR include the following changes:
- [x] Update the copy in the page subtitle with “Select your method for
collecting data into Observability.”
- [x] Remove badges (Quickstart, in a few minutes, setup guide,
integrations, sample data) from all cards
- [x] Remove ‘skip for now’ link
- [x] Change system logs card title with “Stream host system logs” and
card description with “The quickest path to onboard log data from your
own machine or server.”
- [x] Add system icon to system logs card
- [x] Change custom logs card title with “Stream log files” and card
description with “Stream any logs into Elastic in a simple way and
explore their data.”
- [x] Add logging icon to stream logs card
- [x] Remove horizontal line below the cards’ title
- [x] Add “Elastic agent” badge to system logs card and stream log files
- [x] Change CTA button text with ‘Get started’ of both system logs and
stream log files card.
- [x] Remove the cards shadow and add 1px stroke Core/lightShade
- [x] Add 2px stroke Text/accentText around system logs card +
Quickstart badge on top of the card
- [x] Remove ‘sample data’ card
- [x] Change APM card title with “Collect application performance data”
and description with “Collect traces, logs, and metrics from
OpenTelemetry or APM custom agent.”
- [x] Add secondary CTA button to APM card with the text “Get started”
that links to the current flow
- [x] Add Elastic APM logo and Open Telemetry logo to APM card
- [x] Change Kubernetes card title with “Collect Kubernetes clusters
data” and description with “Collect logs and metrics from Kubernetes
clusters with Elastic agent.”
- [x] Add Kubernetes logo to Kubernetes card that links to the current
setup guide
- [x] Add secondary CTA button to Kubernetes card with the text “Get
started”
- [x] Create custom card for integrations with logs from: Amazon
Kinesis, AWS, Apache, Nginx, Google Cloud Platform, Azure and card title
“Explore 300+ ways of ingesting data with our integrations”
- [x] Add secondary CTA button to Integrations card with the text “Start
exploring” that links to the Integrations page with ‘logs’ in the search
bar
- [x] Add quick links to Integrations card to Use sample data (link to
actual flow) + Upload file (link to actual flow) + AWS Firehose (link to
docs:
https://www.elastic.co/guide/en/kinesis/current/aws-firehose-setup-guide.html)
### Missing
- [ ] Link ‘Get started’ button from Stream log files directly to the
stream log files onboarding flow (remove the select logs step)
This will be addressed in a follow up Pr since it's depending on
https://github.com/elastic/kibana/issues/159500.
### Screenshots
#### Before
#### After
---
.../components/app/custom_logs/index.tsx | 5 +-
.../public/components/app/home/index.tsx | 369 ++++++++++--------
.../public/icons/apache.svg | 59 +++
.../public/icons/apm.svg | 14 +
.../public/icons/aws.svg | 16 +
.../public/icons/azure.svg | 10 +
.../public/icons/gcp.svg | 26 ++
.../public/icons/kinesis.svg | 21 +
.../public/icons/kubernetes.svg | 10 +
.../public/icons/logging.svg | 16 +
.../public/icons/nginx.svg | 13 +
.../public/icons/opentelemetry.svg | 1 +
.../public/icons/system.svg | 1 +
.../functional/page_objects/index.ts | 3 +-
.../page_objects/svl_oblt_onboarding_page.ts | 10 +-
.../svl_oblt_onboarding_stream_log_file.ts | 18 +
.../test_suites/observability/landing_page.ts | 13 +-
17 files changed, 428 insertions(+), 177 deletions(-)
create mode 100644 x-pack/plugins/observability_onboarding/public/icons/apache.svg
create mode 100644 x-pack/plugins/observability_onboarding/public/icons/apm.svg
create mode 100644 x-pack/plugins/observability_onboarding/public/icons/aws.svg
create mode 100644 x-pack/plugins/observability_onboarding/public/icons/azure.svg
create mode 100644 x-pack/plugins/observability_onboarding/public/icons/gcp.svg
create mode 100644 x-pack/plugins/observability_onboarding/public/icons/kinesis.svg
create mode 100644 x-pack/plugins/observability_onboarding/public/icons/kubernetes.svg
create mode 100644 x-pack/plugins/observability_onboarding/public/icons/logging.svg
create mode 100644 x-pack/plugins/observability_onboarding/public/icons/nginx.svg
create mode 100644 x-pack/plugins/observability_onboarding/public/icons/opentelemetry.svg
create mode 100644 x-pack/plugins/observability_onboarding/public/icons/system.svg
create mode 100644 x-pack/test_serverless/functional/page_objects/svl_oblt_onboarding_stream_log_file.ts
diff --git a/x-pack/plugins/observability_onboarding/public/components/app/custom_logs/index.tsx b/x-pack/plugins/observability_onboarding/public/components/app/custom_logs/index.tsx
index d886a77bace8a..e178771ca5ed0 100644
--- a/x-pack/plugins/observability_onboarding/public/components/app/custom_logs/index.tsx
+++ b/x-pack/plugins/observability_onboarding/public/components/app/custom_logs/index.tsx
@@ -61,7 +61,10 @@ function AnimatedTransitionsWizard() {
-
+
{i18n.translate(
'xpack.observability_onboarding.title.collectCustomLogs',
diff --git a/x-pack/plugins/observability_onboarding/public/components/app/home/index.tsx b/x-pack/plugins/observability_onboarding/public/components/app/home/index.tsx
index da2441a1d8b7a..0a577419f9883 100644
--- a/x-pack/plugins/observability_onboarding/public/components/app/home/index.tsx
+++ b/x-pack/plugins/observability_onboarding/public/components/app/home/index.tsx
@@ -6,24 +6,53 @@
*/
import {
+ EuiBadge,
+ EuiButton,
+ EuiCard,
EuiFlexGroup,
EuiFlexItem,
+ EuiHorizontalRule,
+ EuiIcon,
+ EuiLink,
EuiSpacer,
- EuiTitle,
EuiText,
- EuiCard,
- EuiHorizontalRule,
- EuiButtonEmpty,
- EuiBadge,
+ EuiTitle,
+ useEuiTheme,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useBreadcrumbs } from '@kbn/observability-shared-plugin/public';
import React from 'react';
-import { useKibanaNavigation } from '../../../hooks/use_kibana_navigation';
+import styled from '@emotion/styled';
import { breadcrumbsApp } from '../../../application/app';
+import { useKibanaNavigation } from '../../../hooks/use_kibana_navigation';
+import apacheIcon from '../../../icons/apache.svg';
+import apmIcon from '../../../icons/apm.svg';
+import awsIcon from '../../../icons/aws.svg';
+import azureIcon from '../../../icons/azure.svg';
+import gcpIcon from '../../../icons/gcp.svg';
+import kinesisIcon from '../../../icons/kinesis.svg';
+import kubernetesIcon from '../../../icons/kubernetes.svg';
+import loggingIcon from '../../../icons/logging.svg';
+import nginxIcon from '../../../icons/nginx.svg';
+import opentelemetryIcon from '../../../icons/opentelemetry.svg';
+import systemIcon from '../../../icons/system.svg';
+
+const StyledItem = styled(EuiFlexItem)`
+ flex-direction: row;
+ &:before {
+ content: '•';
+ width: 5px;
+ height: 5px;
+ margin: 0 20px 0 16px;
+ }
+ > a {
+ min-width: 100%;
+ }
+`;
export function Home() {
useBreadcrumbs([], breadcrumbsApp);
+ const { euiTheme } = useEuiTheme();
const { navigateToKibanaUrl } = useKibanaNavigation();
@@ -43,8 +72,8 @@ export function Home() {
const handleClickSampleData = () => {
navigateToKibanaUrl('/app/home#/tutorial_directory/sampleData');
};
- const handleClickSkip = () => {
- navigateToKibanaUrl('/app/observability/overview');
+ const handleClickUploadFile = () => {
+ navigateToKibanaUrl('/app/home#/tutorial_directory/fileDataViz');
};
return (
@@ -68,7 +97,7 @@ export function Home() {
{i18n.translate('xpack.observability_onboarding.home.description', {
defaultMessage:
- 'Monitor and gain insights across your cloud-native and distributed systems on a single platform.',
+ 'Select your method for collecting data into Observability.',
})}
{i18n.translate(
'xpack.observability_onboarding.card.systemLogs.description1',
{
defaultMessage:
- 'The quickest path to onboard log data and start analysing it straight away.',
- }
- )}
-
-
- {i18n.translate(
- 'xpack.observability_onboarding.card.systemLogs.description2',
- {
- defaultMessage:
- 'Monitor servers, personal computers and more by collecting logs from your machine.',
+ 'The quickest path to onboard log data from your own machine or server.',
}
)}
{i18n.translate(
'xpack.observability_onboarding.card.customLogs.description.text',
{
defaultMessage:
- 'Choose what logs to collect, configure an ingest pipeline, and explore your data.',
+ 'Stream any logs into Elastic in a simple way and explore their data.',
}
)}
;
- }
- },
- align: 'right',
- },
- {
- name: 'Actions',
- actions: [
- {
- name: 'show_more_information',
- description: i18n.translate('xpack.profiling.functionsView.showMoreButton', {
- defaultMessage: `Show more information`,
- }),
- icon: 'inspect',
- color: 'primary',
- type: 'icon',
- onClick: setSelectedRow,
- },
- ],
}
- );
- }
+ return null;
+ }
- const sortedRows = orderBy(
- rows,
- (row) => {
- return sortField === TopNFunctionSortField.Frame
- ? getCalleeFunction(row.frame).toLowerCase()
- : row[sortField];
- },
- [sortDirection]
- ).slice(0, 100);
- return (
- <>
-
-
-
- {
- onSortChange({
- sortDirection: criteria.sort!.direction,
- sortField: criteria.sort!.field as TopNFunctionSortField,
- });
- }}
- sorting={{
- enableAllColumns: true,
- sort: {
- direction: sortDirection,
- field: sortField,
- },
- }}
- />
- {selectedRow && (
- {
- setSelectedRow(undefined);
+ return (
+ <>
+
+
+ {},
+ onChangePage,
+ }}
+ rowHeightsOptions={{ defaultHeight: 'auto' }}
+ toolbarVisibility={{
+ showColumnSelector: false,
+ showKeyboardShortcuts: !isDifferentialView,
+ showDisplaySelector: !isDifferentialView,
+ showFullScreenSelector: !isDifferentialView,
+ showSortSelector: false,
}}
- frame={{
- addressOrLine: selectedRow.frame.AddressOrLine,
- countExclusive: selectedRow.exclusiveCPU,
- countInclusive: selectedRow.inclusiveCPU,
- exeFileName: selectedRow.frame.ExeFileName,
- fileID: selectedRow.frame.FileID,
- frameType: selectedRow.frame.FrameType,
- functionName: selectedRow.frame.FunctionName,
- sourceFileName: selectedRow.frame.SourceFilename,
- sourceLine: selectedRow.frame.SourceLine,
+ virtualizationOptions={{
+ onScroll,
}}
- totalSeconds={totalSeconds ?? 0}
- totalSamples={selectedRow.samples}
- samplingRate={topNFunctions?.SamplingRate ?? 1.0}
+ schemaDetectors={[
+ {
+ type: 'samples',
+ comparator: (a, b, direction) => {
+ const aNumber = parseFloat(a.replace(/,/g, ''));
+ const bNumber = parseFloat(b.replace(/,/g, ''));
+
+ if (aNumber < bNumber) {
+ return direction === 'desc' ? 1 : -1;
+ }
+ if (aNumber > bNumber) {
+ return direction === 'desc' ? -1 : 1;
+ }
+ return 0;
+ },
+ detector: (a) => {
+ return 1;
+ },
+ icon: '',
+ sortTextAsc: 'Low-High',
+ sortTextDesc: 'High-Low',
+ },
+ ]}
/>
- )}
- >
- );
-}
+ {selectedRow && (
+ {
+ setSelectedRow(undefined);
+ }}
+ frame={{
+ addressOrLine: selectedRow.frame.AddressOrLine,
+ countExclusive: selectedRow.selfCPU,
+ countInclusive: selectedRow.totalCPU,
+ exeFileName: selectedRow.frame.ExeFileName,
+ fileID: selectedRow.frame.FileID,
+ frameType: selectedRow.frame.FrameType,
+ functionName: selectedRow.frame.FunctionName,
+ sourceFileName: selectedRow.frame.SourceFilename,
+ sourceLine: selectedRow.frame.SourceLine,
+ }}
+ totalSeconds={totalSeconds}
+ totalSamples={totalCount}
+ samplingRate={topNFunctions?.SamplingRate ?? 1.0}
+ />
+ )}
+ >
+ );
+ }
+);
diff --git a/x-pack/plugins/profiling/public/components/topn_functions/get_label.tsx b/x-pack/plugins/profiling/public/components/topn_functions/label.tsx
similarity index 91%
rename from x-pack/plugins/profiling/public/components/topn_functions/get_label.tsx
rename to x-pack/plugins/profiling/public/components/topn_functions/label.tsx
index 8d04df9cfcd3a..98c340844b7c3 100644
--- a/x-pack/plugins/profiling/public/components/topn_functions/get_label.tsx
+++ b/x-pack/plugins/profiling/public/components/topn_functions/label.tsx
@@ -14,7 +14,7 @@ interface Props {
prepend?: string;
}
-export function GetLabel({ value, prepend, append }: Props) {
+export function Label({ value, prepend, append }: Props) {
const { label, color, icon } = getColorLabel(value);
return (
diff --git a/x-pack/plugins/profiling/public/components/topn_functions/mock/top_n_functions.ts b/x-pack/plugins/profiling/public/components/topn_functions/mock/top_n_functions.ts
new file mode 100644
index 0000000000000..d9c8530ea2eef
--- /dev/null
+++ b/x-pack/plugins/profiling/public/components/topn_functions/mock/top_n_functions.ts
@@ -0,0 +1,115 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { TopNFunctions } from '../../../../common/functions';
+
+export const data = {
+ TotalCount: 4,
+ TopN: [
+ {
+ Rank: 1,
+ Frame: {
+ FrameID: 'VZbhUAtQEfdUptXfb8HvNgAAAAAAsbAE',
+ FileID: 'VZbhUAtQEfdUptXfb8HvNg',
+ FrameType: 4,
+ Inline: false,
+ AddressOrLine: 11644932,
+ FunctionName: '_raw_spin_unlock_irq',
+ FunctionOffset: 0,
+ SourceID: '',
+ SourceLine: 0,
+ ExeFileName: 'vmlinux',
+ CommitHash: '',
+ SourceCodeURL: '',
+ SourceFilename: '',
+ SourcePackageHash: '',
+ SourcePackageURL: '',
+ SamplingRate: 1,
+ FunctionSourceLine: 0,
+ },
+ CountExclusive: 1535,
+ CountInclusive: 1550,
+ Id: 'full;vmlinux;_raw_spin_unlock_irq;',
+ },
+ {
+ Rank: 2,
+ Frame: {
+ FrameID: 'VZbhUAtQEfdUptXfb8HvNgAAAAAAsRk2',
+ FileID: 'VZbhUAtQEfdUptXfb8HvNg',
+ FrameType: 4,
+ Inline: false,
+ AddressOrLine: 11606326,
+ FunctionName: 'syscall_enter_from_user_mode',
+ FunctionOffset: 0,
+ SourceID: '',
+ SourceLine: 0,
+ ExeFileName: 'vmlinux',
+ CommitHash: '',
+ SourceCodeURL: '',
+ SourceFilename: '',
+ SourcePackageHash: '',
+ SourcePackageURL: '',
+ SamplingRate: 1,
+ FunctionSourceLine: 0,
+ },
+ CountExclusive: 1320,
+ CountInclusive: 1610,
+ Id: 'full;vmlinux;syscall_enter_from_user_mode;',
+ },
+ {
+ Rank: 3,
+ Frame: {
+ FrameID: 'VZbhUAtQEfdUptXfb8HvNgAAAAAAsa_W',
+ FileID: 'VZbhUAtQEfdUptXfb8HvNg',
+ FrameType: 4,
+ Inline: false,
+ AddressOrLine: 11644886,
+ FunctionName: '_raw_spin_unlock_irqrestore',
+ FunctionOffset: 0,
+ SourceID: '',
+ SourceLine: 0,
+ ExeFileName: 'vmlinux',
+ CommitHash: '',
+ SourceCodeURL: '',
+ SourceFilename: '',
+ SourcePackageHash: '',
+ SourcePackageURL: '',
+ SamplingRate: 1,
+ FunctionSourceLine: 0,
+ },
+ CountExclusive: 1215,
+ CountInclusive: 1220,
+ Id: 'full;vmlinux;_raw_spin_unlock_irqrestore;',
+ },
+ {
+ Rank: 4,
+ Frame: {
+ FrameID: 'VZbhUAtQEfdUptXfb8HvNgAAAAAAF_dD',
+ FileID: 'VZbhUAtQEfdUptXfb8HvNg',
+ FrameType: 4,
+ Inline: false,
+ AddressOrLine: 1570627,
+ FunctionName: 'audit_filter_syscall',
+ FunctionOffset: 0,
+ SourceID: '',
+ SourceLine: 0,
+ ExeFileName: 'vmlinux',
+ CommitHash: '',
+ SourceCodeURL: '',
+ SourceFilename: '',
+ SourcePackageHash: '',
+ SourcePackageURL: '',
+ SamplingRate: 1,
+ FunctionSourceLine: 0,
+ },
+ CountExclusive: 920,
+ CountInclusive: 1560,
+ Id: 'full;vmlinux;audit_filter_syscall;',
+ },
+ ],
+ SamplingRate: 0.2,
+} as TopNFunctions;
diff --git a/x-pack/plugins/profiling/public/components/topn_functions/sample_stat.tsx b/x-pack/plugins/profiling/public/components/topn_functions/sample_stat.tsx
new file mode 100644
index 0000000000000..4bcc8131c3c9e
--- /dev/null
+++ b/x-pack/plugins/profiling/public/components/topn_functions/sample_stat.tsx
@@ -0,0 +1,41 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import React from 'react';
+import { Label } from './label';
+
+export function SampleStat({
+ samples,
+ diffSamples,
+ totalSamples,
+}: {
+ samples: number;
+ diffSamples?: number;
+ totalSamples: number;
+}) {
+ const samplesLabel = samples.toLocaleString();
+
+ if (diffSamples === undefined || diffSamples === 0 || totalSamples === 0) {
+ return <>{samplesLabel}>;
+ }
+
+ const percentDelta = (diffSamples / (samples - diffSamples)) * 100;
+ const totalPercentDelta = (diffSamples / totalSamples) * 100;
+
+ return (
+
+ {samplesLabel}
+
+
+
+
+
+
+
+ );
+}
diff --git a/x-pack/plugins/profiling/public/components/topn_functions/topn_functions.stories.tsx b/x-pack/plugins/profiling/public/components/topn_functions/topn_functions.stories.tsx
new file mode 100644
index 0000000000000..2ece778600a63
--- /dev/null
+++ b/x-pack/plugins/profiling/public/components/topn_functions/topn_functions.stories.tsx
@@ -0,0 +1,37 @@
+/*
+ * 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 { Meta } from '@storybook/react';
+import React from 'react';
+import { TopNFunctionsView } from '../../views/functions/topn';
+import { MockProfilingDependenciesStorybook } from '../contexts/profiling_dependencies/mock_profiling_dependencies_storybook';
+import { data } from './mock/top_n_functions';
+
+const stories: Meta<{}> = {
+ title: 'Views/TopN functions',
+ component: TopNFunctionsView,
+ decorators: [
+ (StoryComponent, { globals }) => {
+ return (
+ data,
+ }}
+ >
+
+
+ );
+ },
+ ],
+};
+
+export default stories;
+
+export function Examples() {
+ return ;
+}
diff --git a/x-pack/plugins/profiling/public/components/topn_functions/total_samples_stat.tsx b/x-pack/plugins/profiling/public/components/topn_functions/total_samples_stat.tsx
new file mode 100644
index 0000000000000..b4d79c3385560
--- /dev/null
+++ b/x-pack/plugins/profiling/public/components/topn_functions/total_samples_stat.tsx
@@ -0,0 +1,66 @@
+/*
+ * 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 { EuiStat, EuiText } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import React from 'react';
+import { Label } from './label';
+import { scaleValue } from './utils';
+
+interface Props {
+ baselineTotalSamples: number;
+ baselineScaleFactor?: number;
+ comparisonTotalSamples?: number;
+ comparisonScaleFactor?: number;
+}
+
+export function TotalSamplesStat({
+ baselineTotalSamples,
+ baselineScaleFactor,
+ comparisonTotalSamples,
+ comparisonScaleFactor,
+}: Props) {
+ const scaledBaselineTotalSamples = scaleValue({
+ value: baselineTotalSamples,
+ scaleFactor: baselineScaleFactor,
+ });
+
+ const value = scaledBaselineTotalSamples.toLocaleString();
+
+ const sampleHeader = i18n.translate('xpack.profiling.functionsView.totalSampleCountLabel', {
+ defaultMessage: ' Total sample estimate: ',
+ });
+
+ if (comparisonTotalSamples === undefined || comparisonTotalSamples === 0) {
+ return (
+ {value}}
+ description={sampleHeader}
+ />
+ );
+ }
+
+ const scaledComparisonTotalSamples = scaleValue({
+ value: comparisonTotalSamples,
+ scaleFactor: comparisonScaleFactor,
+ });
+
+ const diffSamples = scaledBaselineTotalSamples - scaledComparisonTotalSamples;
+ const percentDelta = (diffSamples / (scaledBaselineTotalSamples - diffSamples)) * 100;
+
+ return (
+
+ {value}
+
+
+ }
+ description={sampleHeader}
+ />
+ );
+}
diff --git a/x-pack/plugins/profiling/public/components/topn_functions/utils.ts b/x-pack/plugins/profiling/public/components/topn_functions/utils.ts
index a11fdffe911a0..84639d100e38e 100644
--- a/x-pack/plugins/profiling/public/components/topn_functions/utils.ts
+++ b/x-pack/plugins/profiling/public/components/topn_functions/utils.ts
@@ -4,6 +4,11 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
+import { keyBy } from 'lodash';
+import { TopNFunctions } from '../../../common/functions';
+import { StackFrameMetadata } from '../../../common/profiling';
+import { calculateImpactEstimates } from '../../utils/calculate_impact_estimates';
+
export function getColorLabel(percent: number) {
const color = percent < 0 ? 'success' : 'danger';
const icon = percent < 0 ? 'sortDown' : 'sortUp';
@@ -12,3 +17,105 @@ export function getColorLabel(percent: number) {
return { color, label, icon: isSmallPercent ? undefined : icon };
}
+
+export function scaleValue({ value, scaleFactor = 1 }: { value: number; scaleFactor?: number }) {
+ return value * scaleFactor;
+}
+
+export interface IFunctionRow {
+ rank: number;
+ frame: StackFrameMetadata;
+ samples: number;
+ selfCPU: number;
+ totalCPU: number;
+ selfCPUPerc: number;
+ totalCPUPerc: number;
+ impactEstimates?: ReturnType;
+ diff?: {
+ rank: number;
+ samples: number;
+ selfCPU: number;
+ totalCPU: number;
+ selfCPUPerc: number;
+ totalCPUPerc: number;
+ };
+}
+
+export function getFunctionsRows({
+ baselineScaleFactor,
+ comparisonScaleFactor,
+ comparisonTopNFunctions,
+ topNFunctions,
+ totalSeconds,
+}: {
+ baselineScaleFactor?: number;
+ comparisonScaleFactor?: number;
+ comparisonTopNFunctions?: TopNFunctions;
+ topNFunctions?: TopNFunctions;
+ totalSeconds: number;
+}): IFunctionRow[] {
+ if (!topNFunctions || !topNFunctions.TotalCount || topNFunctions.TotalCount === 0) {
+ return [];
+ }
+
+ const comparisonDataById = comparisonTopNFunctions
+ ? keyBy(comparisonTopNFunctions.TopN, 'Id')
+ : {};
+
+ return topNFunctions.TopN.filter((topN) => topN.CountExclusive > 0).map((topN, i) => {
+ const comparisonRow = comparisonDataById?.[topN.Id];
+
+ const totalSamples = topN.CountExclusive;
+
+ const topNCountExclusiveScaled = scaleValue({
+ value: totalSamples,
+ scaleFactor: baselineScaleFactor,
+ });
+
+ const totalCPUPerc = (topN.CountInclusive / topNFunctions.TotalCount) * 100;
+ const selfCPUPerc = (topN.CountExclusive / topNFunctions.TotalCount) * 100;
+
+ const impactEstimates =
+ totalSeconds > 0
+ ? calculateImpactEstimates({
+ countExclusive: topN.CountExclusive,
+ countInclusive: topN.CountInclusive,
+ totalSamples,
+ totalSeconds,
+ })
+ : undefined;
+
+ function calculateDiff() {
+ if (comparisonTopNFunctions && comparisonRow) {
+ const comparisonCountExclusiveScaled = scaleValue({
+ value: comparisonRow.CountExclusive,
+ scaleFactor: comparisonScaleFactor,
+ });
+
+ return {
+ rank: topN.Rank - comparisonRow.Rank,
+ samples: topNCountExclusiveScaled - comparisonCountExclusiveScaled,
+ selfCPU: comparisonRow.CountExclusive,
+ selfCPUPerc:
+ selfCPUPerc - (comparisonRow.CountExclusive / comparisonTopNFunctions.TotalCount) * 100,
+ totalCPU: comparisonRow.CountInclusive,
+ totalCPUPerc:
+ totalCPUPerc -
+ (comparisonRow.CountInclusive / comparisonTopNFunctions.TotalCount) * 100,
+ };
+ }
+ }
+
+ return {
+ rank: topN.Rank,
+ frame: topN.Frame,
+ samples: topNCountExclusiveScaled,
+ selfCPU: topN.CountExclusive,
+ selfCPUPerc,
+ totalCPU: topN.CountInclusive,
+ totalCPUPerc,
+ impactEstimates,
+ diff: calculateDiff(),
+ };
+ });
+}
diff --git a/x-pack/plugins/profiling/public/routing/index.tsx b/x-pack/plugins/profiling/public/routing/index.tsx
index 4c2861cf5d220..b27ef5f551199 100644
--- a/x-pack/plugins/profiling/public/routing/index.tsx
+++ b/x-pack/plugins/profiling/public/routing/index.tsx
@@ -202,6 +202,9 @@ const routes = {
),
+ params: t.type({
+ query: t.partial({ pageIndex: toNumberRt }),
+ }),
},
'/functions/differential': {
element: (
@@ -228,6 +231,7 @@ const routes = {
t.partial({
baseline: toNumberRt,
comparison: toNumberRt,
+ pageIndex: toNumberRt,
}),
]),
}),
diff --git a/x-pack/plugins/profiling/public/utils/formatters/as_weight.ts b/x-pack/plugins/profiling/public/utils/formatters/as_weight.ts
index ad64df336171c..539e9669caff4 100644
--- a/x-pack/plugins/profiling/public/utils/formatters/as_weight.ts
+++ b/x-pack/plugins/profiling/public/utils/formatters/as_weight.ts
@@ -5,7 +5,6 @@
* 2.0.
*/
-import { i18n } from '@kbn/i18n';
import { asNumber } from './as_number';
const ONE_POUND_TO_A_KILO = 0.45359237;
@@ -14,11 +13,5 @@ export function asWeight(valueInPounds: number): string {
const lbs = asNumber(valueInPounds);
const kgs = asNumber(Number(valueInPounds * ONE_POUND_TO_A_KILO));
- return i18n.translate('xpack.profiling.formatters.weight', {
- defaultMessage: `{lbs} lbs / {kgs} kg`,
- values: {
- lbs,
- kgs,
- },
- });
+ return `${lbs} lbs / ${kgs} kg`;
}
diff --git a/x-pack/plugins/profiling/public/views/functions/differential_topn/index.tsx b/x-pack/plugins/profiling/public/views/functions/differential_topn/index.tsx
index 1c87247d227bf..66b47825da0cc 100644
--- a/x-pack/plugins/profiling/public/views/functions/differential_topn/index.tsx
+++ b/x-pack/plugins/profiling/public/views/functions/differential_topn/index.tsx
@@ -4,9 +4,10 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
-import { TypeOf } from '@kbn/typed-react-router-config';
-import React from 'react';
+import { EuiDataGridRefProps, EuiDataGridSorting, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import React, { useRef } from 'react';
+import { GridOnScrollProps } from 'react-window';
+import { TopNFunctionSortField } from '../../../../common/functions';
import { AsyncComponent } from '../../../components/async_component';
import { useProfilingDependencies } from '../../../components/contexts/profiling_dependencies/use_profiling_dependencies';
import {
@@ -15,32 +16,31 @@ import {
NormalizationOptions,
} from '../../../components/normalization_menu';
import { PrimaryAndComparisonSearchBar } from '../../../components/primary_and_comparison_search_bar';
-import { TopNFunctionsTable } from '../../../components/topn_functions';
+import { TopNFunctionsGrid } from '../../../components/topn_functions';
import { useProfilingParams } from '../../../hooks/use_profiling_params';
import { useProfilingRouter } from '../../../hooks/use_profiling_router';
import { useProfilingRoutePath } from '../../../hooks/use_profiling_route_path';
import { useTimeRange } from '../../../hooks/use_time_range';
import { useTimeRangeAsync } from '../../../hooks/use_time_range_async';
-import { ProfilingRoutes } from '../../../routing';
export function DifferentialTopNFunctionsView() {
+ const baseGridRef = useRef(null);
+ const comparisonGridRef = useRef(null);
+ const { query } = useProfilingParams('/functions/differential');
const {
- path,
- query,
- query: {
- rangeFrom,
- rangeTo,
- kuery,
- sortDirection,
- sortField,
- comparisonKuery,
- normalizationMode,
- comparisonRangeFrom,
- comparisonRangeTo,
- baseline = 1,
- comparison = 1,
- },
- } = useProfilingParams('/functions/differential');
+ rangeFrom,
+ rangeTo,
+ kuery,
+ sortDirection,
+ sortField,
+ comparisonKuery,
+ normalizationMode,
+ comparisonRangeFrom,
+ comparisonRangeTo,
+ baseline = 1,
+ comparison = 1,
+ pageIndex = 0,
+ } = query;
const timeRange = useTimeRange({ rangeFrom, rangeTo });
const comparisonTimeRange = useTimeRange({
@@ -142,6 +142,38 @@ export function DifferentialTopNFunctionsView() {
});
}
+ function handleBaseGridScroll(scroll: GridOnScrollProps) {
+ if (comparisonGridRef?.current?.scrollTo) {
+ comparisonGridRef.current.scrollTo({
+ scrollTop: scroll.scrollTop,
+ });
+ }
+ }
+
+ function handleComparisonGridScroll(scroll: GridOnScrollProps) {
+ if (baseGridRef?.current?.scrollTo) {
+ baseGridRef.current.scrollTo({ scrollTop: scroll.scrollTop });
+ }
+ }
+
+ function handlePageChange(nextPage: number) {
+ profilingRouter.push('/functions/differential', {
+ path: {},
+ query: { ...query, pageIndex: nextPage },
+ });
+ }
+
+ function handleSortChange(sorting: EuiDataGridSorting['columns'][0]) {
+ profilingRouter.push('/functions/differential', {
+ path: {},
+ query: {
+ ...query,
+ sortField: sorting.id as TopNFunctionSortField,
+ sortDirection: sorting.direction,
+ },
+ });
+ }
+
return (
<>
@@ -159,43 +191,27 @@ export function DifferentialTopNFunctionsView() {
- {
- profilingRouter.push(routePath, {
- path,
- query: {
- ...query,
- sortField: nextSort.sortField,
- sortDirection: nextSort.sortDirection,
- },
- });
- }}
totalSeconds={timeRange.inSeconds.end - timeRange.inSeconds.start}
isDifferentialView={true}
- baselineScaleFactor={isNormalizedByTime ? baselineTime : baseline}
onFrameClick={handleOnFrameClick}
+ baselineScaleFactor={isNormalizedByTime ? baselineTime : baseline}
+ onScroll={handleBaseGridScroll}
+ pageIndex={pageIndex}
+ onChangePage={handlePageChange}
+ sortField={sortField}
+ sortDirection={sortDirection}
+ onChangeSort={handleSortChange}
/>
{comparisonTimeRange.inSeconds.start && comparisonTimeRange.inSeconds.end ? (
- {
- profilingRouter.push(routePath, {
- path,
- query: {
- ...(query as TypeOf['query']),
- sortField: nextSort.sortField,
- sortDirection: nextSort.sortDirection,
- },
- });
- }}
+
diff --git a/x-pack/plugins/profiling/public/views/functions/topn/index.tsx b/x-pack/plugins/profiling/public/views/functions/topn/index.tsx
index 768f5256fe3c3..74e23d4c172ce 100644
--- a/x-pack/plugins/profiling/public/views/functions/topn/index.tsx
+++ b/x-pack/plugins/profiling/public/views/functions/topn/index.tsx
@@ -4,23 +4,20 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import { EuiDataGridSorting, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React from 'react';
+import { TopNFunctionSortField } from '../../../../common/functions';
import { AsyncComponent } from '../../../components/async_component';
import { useProfilingDependencies } from '../../../components/contexts/profiling_dependencies/use_profiling_dependencies';
-import { TopNFunctionsTable } from '../../../components/topn_functions';
+import { TopNFunctionsGrid } from '../../../components/topn_functions';
import { useProfilingParams } from '../../../hooks/use_profiling_params';
import { useProfilingRouter } from '../../../hooks/use_profiling_router';
-import { useProfilingRoutePath } from '../../../hooks/use_profiling_route_path';
import { useTimeRange } from '../../../hooks/use_time_range';
import { useTimeRangeAsync } from '../../../hooks/use_time_range_async';
export function TopNFunctionsView() {
- const {
- path,
- query,
- query: { rangeFrom, rangeTo, kuery, sortDirection, sortField },
- } = useProfilingParams('/functions/topn');
+ const { query } = useProfilingParams('/functions/topn');
+ const { rangeFrom, rangeTo, kuery, sortDirection, sortField, pageIndex = 0 } = query;
const timeRange = useTimeRange({ rangeFrom, rangeTo });
@@ -42,8 +39,6 @@ export function TopNFunctionsView() {
[timeRange.inSeconds.start, timeRange.inSeconds.end, kuery, fetchTopNFunctions]
);
- const routePath = useProfilingRoutePath() as '/functions/topn';
-
const profilingRouter = useProfilingRouter();
function handleOnFrameClick(functionName: string) {
@@ -53,30 +48,41 @@ export function TopNFunctionsView() {
});
}
+ function handlePageChange(nextPage: number) {
+ profilingRouter.push('/functions/topn', {
+ path: {},
+ query: { ...query, pageIndex: nextPage },
+ });
+ }
+
+ function handleSortChange(sorting: EuiDataGridSorting['columns'][0]) {
+ profilingRouter.push('/functions/topn', {
+ path: {},
+ query: {
+ ...query,
+ sortField: sorting.id as TopNFunctionSortField,
+ sortDirection: sorting.direction,
+ },
+ });
+ }
+
return (
<>
-
+
- {
- profilingRouter.push(routePath, {
- path,
- query: {
- ...query,
- sortField: nextSort.sortField,
- sortDirection: nextSort.sortDirection,
- },
- });
- }}
totalSeconds={timeRange.inSeconds.end - timeRange.inSeconds.start}
isDifferentialView={false}
onFrameClick={handleOnFrameClick}
+ pageIndex={pageIndex}
+ onChangePage={handlePageChange}
+ sortField={sortField}
+ sortDirection={sortDirection}
+ onChangeSort={handleSortChange}
/>
diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json
index beac2e361bd86..70580e4b6b098 100644
--- a/x-pack/plugins/translations/translations/fr-FR.json
+++ b/x-pack/plugins/translations/translations/fr-FR.json
@@ -27203,7 +27203,6 @@
"xpack.osquery.scheduledQueryErrorsTable.errorColumnTitle": "Erreur",
"xpack.osquery.viewSavedQuery.prebuiltInfo": "Il s'agit d'une requête Elastic prédéfinie, et elle ne peut pas être modifiée.",
"xpack.profiling.flameGraphTooltip.valueLabel": "Comparaison entre {value} et {comparison}",
- "xpack.profiling.formatters.weight": "{lbs} lb / {kgs} kg",
"xpack.profiling.frameInformationWindow.missingSymbols.native": "Pour voir les noms de fonction et les numéros de ligne dans les traces d'applications écrites dans des langages de programmation qui se compilent en code natif (C, C++, Rust, Go, etc.), vous devez transmettre des symboles au cluster à l'aide du binaire de profilage Elastic. {readMore} ou téléchargez le binaire ci-dessous.",
"xpack.profiling.maxValue": "Max : {max}",
"xpack.profiling.noDataConfig.action.dataRetention": "Les coûts normaux de stockage des données s'appliquent aux données de profilage stockées dans Elasticsearch. En savoir plus sur {dataRetentionLink}.",
@@ -27273,7 +27272,6 @@
"xpack.profiling.functionsView.newLabel": "Nouveauté",
"xpack.profiling.functionsView.rankColumnLabel": "Rang",
"xpack.profiling.functionsView.samplesColumnLabel": "Échantillons (établ.)",
- "xpack.profiling.functionsView.showMoreButton": "Afficher des informations supplémentaires",
"xpack.profiling.functionsView.totalSampleCountLabel": " Total estimation d'échantillons : ",
"xpack.profiling.header.betaBadgeTooltip": "Ce module n'est pas disponible. Nous vous remercions de bien vouloir nous aider en nous signalant tout bug.",
"xpack.profiling.header.giveFeedbackLink": "Donner un retour",
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 5d19f5921d0dd..c9ec4c45294fe 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -27189,7 +27189,6 @@
"xpack.osquery.scheduledQueryErrorsTable.errorColumnTitle": "エラー",
"xpack.osquery.viewSavedQuery.prebuiltInfo": "これは既製のElasticクエリであり、編集できません。",
"xpack.profiling.flameGraphTooltip.valueLabel": "{value} vs {comparison}",
- "xpack.profiling.formatters.weight": "{lbs} lbs / {kgs} kg",
"xpack.profiling.frameInformationWindow.missingSymbols.native": "ネイティブコード(C、C++、Rust、Goなど)にコンパイルするプログラミング言語で作成されたアプリケーションのトレースで関数名と行番号を表示するには、elastic-profilingバイナリを使用して、シンボルをクラスターにプッシュする必要があります。{readMore}か、下のバイナリをダウンロードしてください。",
"xpack.profiling.maxValue": "最大:{max}",
"xpack.profiling.noDataConfig.action.dataRetention": "Elasticsearchに格納されたプロファイリングデータには、標準データ保存コストが適用されます。{dataRetentionLink}の詳細をご覧ください。",
@@ -27259,7 +27258,6 @@
"xpack.profiling.functionsView.newLabel": "新規",
"xpack.profiling.functionsView.rankColumnLabel": "ランク",
"xpack.profiling.functionsView.samplesColumnLabel": "サンプル(推定)",
- "xpack.profiling.functionsView.showMoreButton": "詳細を表示",
"xpack.profiling.functionsView.totalSampleCountLabel": " 合計サンプル推定:",
"xpack.profiling.header.betaBadgeTooltip": "このモジュールはGAではありません。不具合が発生したら報告してください。",
"xpack.profiling.header.giveFeedbackLink": "フィードバックを作成する",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 1ff38850bff97..976b48b99e415 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -27187,7 +27187,6 @@
"xpack.osquery.scheduledQueryErrorsTable.errorColumnTitle": "错误",
"xpack.osquery.viewSavedQuery.prebuiltInfo": "这是预构建的 Elastic 查询,无法编辑。",
"xpack.profiling.flameGraphTooltip.valueLabel": "{value} 与 {comparison}",
- "xpack.profiling.formatters.weight": "{lbs} lbs / {kgs} kg",
"xpack.profiling.frameInformationWindow.missingSymbols.native": "在跟踪编译为本机代码的编程语言(C、C++、Rust、Go 等)所编写的应用程序中,要查看函数名称和行号,您需要使用 Elastic 分析二进制文件将符号推送到集群。{readMore},或在下面下载二进制文件。",
"xpack.profiling.maxValue": "最大值:{max}",
"xpack.profiling.noDataConfig.action.dataRetention": "分析 Elasticsearch 中存储的数据时,正常数据存储成本适用。详细了解 {dataRetentionLink}。",
@@ -27257,7 +27256,6 @@
"xpack.profiling.functionsView.newLabel": "新建",
"xpack.profiling.functionsView.rankColumnLabel": "排名",
"xpack.profiling.functionsView.samplesColumnLabel": "样例(估计)",
- "xpack.profiling.functionsView.showMoreButton": "显示更多信息",
"xpack.profiling.functionsView.totalSampleCountLabel": " 总样例数估值:",
"xpack.profiling.header.betaBadgeTooltip": "此模块不是 GA 版。请通过报告错误来帮助我们。",
"xpack.profiling.header.giveFeedbackLink": "反馈",
From 45a483f49643bcca4ff130d9f100c38a1a2181e7 Mon Sep 17 00:00:00 2001
From: Juan Pablo Djeredjian
Date: Fri, 14 Jul 2023 15:49:42 +0200
Subject: [PATCH 20/50] [Security Solution] Intercept individual package
installation via Fleet (#161859)
## Summary
During Cypress tests, intercept `POST
/api/fleet/epm/packages/security_detection_engine/*`.
This is the endpoint used when a specific `security_detection_engine`
package is set to be used via the
`--xpack.securitySolution.prebuiltRulesPackageVersion` config flag,
which is used to test by the TRADE team.
This PR updates the test to account for that flow.
### For maintainers
- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
---
...built_rules_install_update_workflows.cy.ts | 77 ++++++++++++++-----
.../cypress/tasks/api_calls/prebuilt_rules.ts | 1 +
2 files changed, 60 insertions(+), 18 deletions(-)
diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/prebuilt_rules_install_update_workflows.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/prebuilt_rules_install_update_workflows.cy.ts
index bec5b77de52c9..355611ba42eea 100644
--- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/prebuilt_rules_install_update_workflows.cy.ts
+++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/prebuilt_rules_install_update_workflows.cy.ts
@@ -45,35 +45,57 @@ describe('Detection rules, Prebuilt Rules Installation and Update workflow', ()
describe('Installation of prebuilt rules package via Fleet', () => {
beforeEach(() => {
- cy.intercept('POST', '/api/fleet/epm/packages/_bulk*').as('installPackage');
+ cy.intercept('POST', '/api/fleet/epm/packages/_bulk*').as('installPackageBulk');
+ cy.intercept('POST', '/api/fleet/epm/packages/security_detection_engine/*').as(
+ 'installPackage'
+ );
waitForRulesTableToBeLoaded();
});
it('should install package from Fleet in the background', () => {
/* Assert that the package in installed from Fleet by checking that
/* the installSource is "registry", as opposed to "bundle" */
- cy.wait('@installPackage', {
+ cy.wait('@installPackageBulk', {
timeout: 60000,
- }).then(({ response }) => {
- cy.wrap(response?.statusCode).should('eql', 200);
-
- const packages = response?.body.items.map(({ name, result }: BulkInstallPackageInfo) => ({
- name,
- installSource: result.installSource,
- }));
-
- expect(packages.length).to.have.greaterThan(0);
- expect(packages).to.deep.include.members([
- { name: 'security_detection_engine', installSource: 'registry' },
- ]);
+ }).then(({ response: bulkResponse }) => {
+ cy.wrap(bulkResponse?.statusCode).should('eql', 200);
+
+ const packages = bulkResponse?.body.items.map(
+ ({ name, result }: BulkInstallPackageInfo) => ({
+ name,
+ installSource: result.installSource,
+ })
+ );
+
+ const packagesBulkInstalled = packages.map(({ name }: { name: string }) => name);
+
+ // Under normal flow the package is installed via the Fleet bulk install API.
+ // However, for testing purposes the package can be installed via the Fleet individual install API,
+ // so we need to intercept and wait for that request as well.
+ if (!packagesBulkInstalled.includes('security_detection_engine')) {
+ // Should happen only during testing when the `xpack.securitySolution.prebuiltRulesPackageVersion` flag is set
+ cy.wait('@installPackage').then(({ response }) => {
+ cy.wrap(response?.statusCode).should('eql', 200);
+ cy.wrap(response?.body)
+ .should('have.property', 'items')
+ .should('have.length.greaterThan', 0);
+ cy.wrap(response?.body)
+ .should('have.property', '_meta')
+ .should('have.property', 'install_source')
+ .should('eql', 'registry');
+ });
+ } else {
+ // Normal flow, install via the Fleet bulk install API
+ expect(packages.length).to.have.greaterThan(0);
+ expect(packages).to.deep.include.members([
+ { name: 'security_detection_engine', installSource: 'registry' },
+ ]);
+ }
});
});
it('should install rules from the Fleet package when user clicks on CTA', () => {
- /* Retrieve how many rules were installed from the Fleet package */
- cy.wait('@installPackage', {
- timeout: 60000,
- }).then(() => {
+ const getRulesAndAssertNumberInstalled = () => {
getRuleAssets().then((response) => {
const ruleIds = response.body.hits.hits.map(
(hit: { _source: { ['security-rule']: Rule } }) => hit._source['security-rule'].rule_id
@@ -87,6 +109,25 @@ describe('Detection rules, Prebuilt Rules Installation and Update workflow', ()
.should('be.visible')
.should('have.text', `${numberOfRulesToInstall} rules installed successfully.`);
});
+ };
+ /* Retrieve how many rules were installed from the Fleet package */
+ /* See comments in test above for more details */
+ cy.wait('@installPackageBulk', {
+ timeout: 60000,
+ }).then(({ response: bulkResponse }) => {
+ cy.wrap(bulkResponse?.statusCode).should('eql', 200);
+
+ const packagesBulkInstalled = bulkResponse?.body.items.map(
+ ({ name }: { name: string }) => name
+ );
+
+ if (!packagesBulkInstalled.includes('security_detection_engine')) {
+ cy.wait('@installPackage').then(() => {
+ getRulesAndAssertNumberInstalled();
+ });
+ } else {
+ getRulesAndAssertNumberInstalled();
+ }
});
});
});
diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/prebuilt_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/prebuilt_rules.ts
index 1a079cb43ec20..e175fe3345f80 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/prebuilt_rules.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/prebuilt_rules.ts
@@ -170,6 +170,7 @@ export const getRuleAssets = (index: string | undefined = '.kibana_security_solu
/* during e2e tests, and allow for manual installation of mock rules instead. */
export const preventPrebuiltRulesPackageInstallation = () => {
cy.intercept('POST', '/api/fleet/epm/packages/_bulk*', {});
+ cy.intercept('POST', '/api/fleet/epm/packages/security_detection_engine/*', {});
};
/**
From dcf1e7606ac0335a6ae344d094ceca508af9c18e Mon Sep 17 00:00:00 2001
From: Rodney Norris
Date: Fri, 14 Jul 2023 08:59:34 -0500
Subject: [PATCH 21/50] [Enterprise Search] feat: rebrand ent-search content
Enterprise Search to Search (#161902)
## Summary
Updated usages of "Enterprise Search" to "Search" in the content
application
---
.../components/new_index/empty_state.tsx | 2 +-
.../search_index/connector/connector_configuration.tsx | 6 +++---
.../native_connector_configuration_config.tsx | 3 +--
.../components/search_index/generate_api_key_panel.tsx | 2 +-
.../pipelines/ingest_pipelines/ingest_pipeline_flyout.tsx | 4 ++--
.../pipelines/ml_inference/configure_pipeline.tsx | 3 +--
.../search_index/pipelines/pipeline_json_badges.tsx | 2 +-
.../components/search_index/pipelines/pipelines.tsx | 6 +++---
.../pipelines/pipelines_json_configurations.tsx | 2 +-
.../components/search_indices/search_indices.tsx | 8 ++++----
.../components/settings/settings.tsx | 2 +-
.../add_content_empty_prompt.test.tsx | 2 +-
.../add_content_empty_prompt/add_content_empty_prompt.tsx | 4 ++--
.../getting_started_steps/getting_started_steps.test.tsx | 2 +-
.../getting_started_steps/getting_started_steps.tsx | 4 ++--
15 files changed, 25 insertions(+), 27 deletions(-)
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/empty_state.tsx
index 8a6f66238b07f..482274d27d06a 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/empty_state.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/empty_state.tsx
@@ -32,7 +32,7 @@ export const SearchIndexEmptyState: React.FC = () => {
{i18n.translate('xpack.enterpriseSearch.content.newIndex.emptyState.description', {
defaultMessage:
- 'Data you add in Enterprise Search is called a search index and it’s searchable in both App Search and Workplace Search. Now you can use your connectors in App Search and your web crawlers in Workplace Search.',
+ 'Data you add in Search is called a search index and it’s searchable in both App Search and Workplace Search. Now you can use your connectors in App Search and your web crawlers in Workplace Search.',
})}
}
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx
index 9cd4802ed2be4..6f4ff29b9e9b8 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx
@@ -132,7 +132,7 @@ export const ConnectorConfiguration: React.FC = () => {
@@ -229,7 +229,7 @@ service_type: "${index.connector.service_type || 'changeme'}"
'xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.connectorConnected',
{
defaultMessage:
- 'Your connector {name} has connected to Enterprise Search successfully.',
+ 'Your connector {name} has connected to Search successfully.',
values: { name: index.connector.name },
}
)}
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration_config.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration_config.tsx
index 75aea55e98bb5..53a6166ad34e5 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration_config.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration_config.tsx
@@ -75,8 +75,7 @@ export const NativeConnectorConfigurationConfig: React.FC<
title={i18n.translate(
'xpack.enterpriseSearch.content.indices.configurationConnector.nativeConnector.connectorConnected',
{
- defaultMessage:
- 'Your connector {name} has connected to Enterprise Search successfully.',
+ defaultMessage: 'Your connector {name} has connected to Search successfully.',
values: { name: nativeConnector.name },
}
)}
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx
index 28a09ffa16ea3..0de71b834b6f0 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx
@@ -135,7 +135,7 @@ export const GenerateApiKeyPanel: React.FC = () => {
onChange={(event) => setOptimizedRequest(event.target.checked)}
label={i18n.translate(
'xpack.enterpriseSearch.content.overview.optimizedRequest.label',
- { defaultMessage: 'View Enterprise Search optimized request' }
+ { defaultMessage: 'View Search optimized request' }
)}
checked={optimizedRequest}
/>
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ingest_pipelines/ingest_pipeline_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ingest_pipelines/ingest_pipeline_flyout.tsx
index dc6dd019128d4..1dcffe443855e 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ingest_pipelines/ingest_pipeline_flyout.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ingest_pipelines/ingest_pipeline_flyout.tsx
@@ -160,7 +160,7 @@ export const IngestPipelineFlyout: React.FC = ({
'xpack.enterpriseSearch.content.index.pipelines.ingestFlyout.modalBodyConnectorText',
{
defaultMessage:
- 'This pipeline runs automatically on all Crawler and Connector indices created through Enterprise Search.',
+ 'This pipeline runs automatically on all Crawler and Connector indices created through Search.',
}
)
)}
@@ -172,7 +172,7 @@ export const IngestPipelineFlyout: React.FC = ({
{i18n.translate(
'xpack.enterpriseSearch.content.index.pipelines.ingestFlyout.modalIngestLinkLabel',
{
- defaultMessage: 'Learn more about Enterprise Search ingest pipelines',
+ defaultMessage: 'Learn more about Search ingest pipelines',
}
)}
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx
index 7568481a66d5f..daac178cd8d6b 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx
@@ -278,8 +278,7 @@ export const ConfigurePipeline: React.FC = () => {
{i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.docsLink',
{
- defaultMessage:
- 'Learn more about importing and using ML models in Enterprise Search',
+ defaultMessage: 'Learn more about importing and using ML models in Search',
}
)}
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipeline_json_badges.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipeline_json_badges.tsx
index 16365012003b0..1ff6364ff53df 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipeline_json_badges.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipeline_json_badges.tsx
@@ -71,7 +71,7 @@ const SharedPipelineBadge: React.FC = () => (
position="top"
content={i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.tabs.jsonConfigurations.shared.description',
- { defaultMessage: 'This pipeline is shared across all Enterprise Search ingestion methods' }
+ { defaultMessage: 'This pipeline is shared across all Search ingestion methods' }
)}
>
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines.tsx
index 3ef16e4361c8b..800c1fc0abbf1 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines.tsx
@@ -129,7 +129,7 @@ export const SearchIndexPipelines: React.FC = () => {
{i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.ingestionPipeline.docLink',
{
- defaultMessage: 'Learn more about using pipelines in Enterprise Search',
+ defaultMessage: 'Learn more about using pipelines in Search',
}
)}
@@ -218,7 +218,7 @@ export const SearchIndexPipelines: React.FC = () => {
'xpack.enterpriseSearch.content.indices.pipelines.mlInferencePipelines.subtitleAPIindex',
{
defaultMessage:
- "Inference pipelines will be run as processors from the Enterprise Search Ingest Pipeline. In order to use these pipelines on API-based indices you'll need to reference the {pipelineName} pipeline in your API requests.",
+ "Inference pipelines will be run as processors from the Search Ingest Pipeline. In order to use these pipelines on API-based indices you'll need to reference the {pipelineName} pipeline in your API requests.",
values: {
pipelineName,
},
@@ -228,7 +228,7 @@ export const SearchIndexPipelines: React.FC = () => {
'xpack.enterpriseSearch.content.indices.pipelines.mlInferencePipelines.subtitle',
{
defaultMessage:
- 'Inference pipelines will be run as processors from the Enterprise Search Ingest Pipeline',
+ 'Inference pipelines will be run as processors from the Search Ingest Pipeline',
}
)
}
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines_json_configurations.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines_json_configurations.tsx
index eade81733a9eb..61ecd35d0b6b4 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines_json_configurations.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/pipelines_json_configurations.tsx
@@ -62,7 +62,7 @@ export const PipelinesJSONConfigurations: React.FC = () => {
{i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.tabs.jsonConfigurations.ingestionPipelines.docLink',
{
- defaultMessage: 'Learn more about how Enterprise Search uses ingest pipelines',
+ defaultMessage: 'Learn more about how Search uses ingest pipelines',
}
)}
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx
index d7d67ab459df2..b5db0c4f08559 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx
@@ -87,7 +87,7 @@ export const SearchIndices: React.FC = () => {
? ''
: hasNoIndices
? i18n.translate('xpack.enterpriseSearch.content.searchIndices.searchIndices.emptyPageTitle', {
- defaultMessage: 'Welcome to Enterprise Search',
+ defaultMessage: 'Welcome to Search',
})
: i18n.translate('xpack.enterpriseSearch.content.searchIndices.searchIndices.pageTitle', {
defaultMessage: 'Elasticsearch indices',
@@ -131,14 +131,14 @@ export const SearchIndices: React.FC = () => {
{title ||
i18n.translate('xpack.enterpriseSearch.overview.emptyState.heading', {
- defaultMessage: 'Add content to Enterprise Search',
+ defaultMessage: 'Add content to Search',
})}
@@ -81,7 +81,7 @@ export const AddContentEmptyPrompt: React.FC = ({ title, butto
{buttonLabel ||
i18n.translate('xpack.enterpriseSearch.overview.emptyState.buttonTitle', {
- defaultMessage: 'Add content to Enterprise Search',
+ defaultMessage: 'Add content to Search',
})}
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.test.tsx
index f6d15b3157cad..6a3fec72da346 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.test.tsx
@@ -44,7 +44,7 @@ describe('GettingStartedSteps', () => {
...rest,
}));
- expect(steps[0].title).toEqual('Add your documents to Enterprise Search');
+ expect(steps[0].title).toEqual('Add your documents to Search');
expect(steps[0].status).toEqual('current');
expect(steps[0].children.find(IconRow).length).toEqual(1);
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.tsx
index 0cd7f130a9d83..d8b49cc48fca3 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.tsx
@@ -29,7 +29,7 @@ export const GettingStartedSteps: React.FC = ({ step =
{
title: i18n.translate(
'xpack.enterpriseSearch.overview.gettingStartedSteps.addData.title',
- { defaultMessage: 'Add your documents to Enterprise Search' }
+ { defaultMessage: 'Add your documents to Search' }
),
children: (
<>
@@ -39,7 +39,7 @@ export const GettingStartedSteps: React.FC = ({ step =
'xpack.enterpriseSearch.overview.gettingStartedSteps.addData.message',
{
defaultMessage:
- 'Add your data to Enterprise Search. You can crawl website content with the Elastic web crawler, connect your existing application with Elasticsearch API endpoints, or use connectors to directly add third party content from providers like Google Drive, Microsoft Sharepoint and more.',
+ 'Add your data to Search. You can crawl website content with the Elastic web crawler, connect your existing application with Elasticsearch API endpoints, or use connectors to directly add third party content from providers like Google Drive, Microsoft Sharepoint and more.',
}
)}
From c526b3c84b885e0cc9580bdca3082a43692a3c0f Mon Sep 17 00:00:00 2001
From: Jeramy Soucy
Date: Fri, 14 Jul 2023 10:12:26 -0400
Subject: [PATCH 22/50] Changes the theme override tip from EuiIconTip to
EuiToolTip (#161703)
## Summary
Removes the lock tip icon in favor of a standard tool tip for the theme
mode keypad menu.
Previous render:
New Render:
### Tests
-
`x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx`
### Manual Testing
- Start Elasricsearch, and start Kibana with an empty
kibana.yml/kibana.dev.yml
- Navigate to the _Edit profile_ screen via the profile/avatar button in
the top right portion of the uI
- Verify the theme settings appear as a KeyPadMenu and function as
expected
- Modify the kibana.yml (or kibana.dev.yml) with the line
`uiSettings.overrides.theme:darkMode: true`
- Refresh Kibana and verify that the dark theme is rendered and the
theme settings are disabled and includes a tooltip explaining why the
mode setting is locked
---
.../user_profile/user_profile.test.tsx | 8 +-
.../user_profile/user_profile.tsx | 142 ++++++++----------
2 files changed, 70 insertions(+), 80 deletions(-)
diff --git a/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx b/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx
index b5748a16d1e43..3263b4db80c66 100644
--- a/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx
+++ b/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx
@@ -257,7 +257,7 @@ describe('useUserProfileForm', () => {
);
- const overrideMsg = testWrapper.find('EuiText[data-test-subj="themeOverrideMessage"]');
+ const overrideMsg = testWrapper.find('EuiToolTip[data-test-subj="themeOverrideTooltip"]');
expect(overrideMsg).toHaveLength(0);
const themeMenu = testWrapper.find('EuiKeyPadMenu[data-test-subj="themeMenu"]');
@@ -343,8 +343,9 @@ describe('useUserProfileForm', () => {
);
- const overrideMsg = testWrapper.find('EuiIconTip[data-test-subj="themeOverrideTooltip"]');
+ const overrideMsg = testWrapper.find('EuiToolTip[data-test-subj="themeOverrideTooltip"]');
expect(overrideMsg).toHaveLength(1);
+ expect(overrideMsg.getElement().props.content).not.toEqual('');
const themeMenu = testWrapper.find('EuiKeyPadMenu[data-test-subj="themeMenu"]');
expect(themeMenu).toHaveLength(1);
@@ -380,8 +381,9 @@ describe('useUserProfileForm', () => {
);
- const overrideMsg = testWrapper.find('EuiIconTip[data-test-subj="themeOverrideTooltip"]');
+ const overrideMsg = testWrapper.find('EuiToolTip[data-test-subj="themeOverrideTooltip"]');
expect(overrideMsg).toHaveLength(1);
+ expect(overrideMsg.getElement().props.content).not.toEqual('');
const themeMenu = testWrapper.find('EuiKeyPadMenu[data-test-subj="themeMenu"]');
expect(themeMenu).toHaveLength(1);
diff --git a/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx b/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx
index e9bb4b23af997..2632f73e99d07 100644
--- a/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx
+++ b/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx
@@ -26,6 +26,7 @@ import {
EuiPopover,
EuiSpacer,
EuiText,
+ EuiToolTip,
useEuiTheme,
useGeneratedHtmlId,
} from '@elastic/eui';
@@ -194,7 +195,7 @@ const UserSettingsEditor: FunctionComponent = ({
icon: string;
}
- const themeKeyPadMenuItem = ({ id, label, icon }: ThemeKeyPadItem) => {
+ const themeItem = ({ id, label, icon }: ThemeKeyPadItem) => {
return (
= ({
);
};
+ const themeMenu = (themeOverridden: boolean) => {
+ const themeKeyPadMenu = (
+
+
+
+ ),
+ }}
+ >
+ {themeItem({
+ id: '',
+ label: i18n.translate('xpack.security.accountManagement.userProfile.defaultModeButton', {
+ defaultMessage: 'Space default',
+ }),
+ icon: 'spaces',
+ })}
+ {themeItem({
+ id: 'light',
+ label: i18n.translate('xpack.security.accountManagement.userProfile.lightModeButton', {
+ defaultMessage: 'Light',
+ }),
+ icon: 'sun',
+ })}
+ {themeItem({
+ id: 'dark',
+ label: i18n.translate('xpack.security.accountManagement.userProfile.darkModeButton', {
+ defaultMessage: 'Dark',
+ }),
+ icon: 'moon',
+ })}
+
+ );
+ return themeOverridden ? (
+
+ }
+ >
+ {themeKeyPadMenu}
+
+ ) : (
+ themeKeyPadMenu
+ );
+ };
+
return (
= ({
/>
}
>
-
-
-
-
-
-
- {renderHelpText(isThemeOverridden)}
-
- }
- fullWidth
- >
-
- {themeKeyPadMenuItem({
- id: '',
- label: i18n.translate(
- 'xpack.security.accountManagement.userProfile.defaultModeButton',
- {
- defaultMessage: 'Space default',
- }
- ),
- icon: 'spaces',
- })}
- {themeKeyPadMenuItem({
- id: 'light',
- label: i18n.translate('xpack.security.accountManagement.userProfile.lightModeButton', {
- defaultMessage: 'Light',
- }),
- icon: 'sun',
- })}
- {themeKeyPadMenuItem({
- id: 'dark',
- label: i18n.translate('xpack.security.accountManagement.userProfile.darkModeButton', {
- defaultMessage: 'Dark',
- }),
- icon: 'moon',
- })}
-
+
+ {themeMenu(isThemeOverridden)}
);
@@ -989,30 +1001,6 @@ export const SaveChangesBottomBar: FunctionComponent = () => {
);
};
-function renderHelpText(isOverridden: boolean) {
- if (isOverridden) {
- return (
-
- }
- />
- );
- }
-}
-
function determineIfThemeOverridden(settingsClient: IUiSettingsClient): {
isThemeOverridden: boolean;
isOverriddenThemeDarkMode: boolean;
From 9746a50a011db6ffeb5fdd773140ae1d635d2d61 Mon Sep 17 00:00:00 2001
From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com>
Date: Fri, 14 Jul 2023 16:29:50 +0200
Subject: [PATCH 23/50] [Fleet] enable flaky agent upgrade test (#161933)
## Summary
Closes https://github.com/elastic/kibana/issues/161557
Enabled flaky test
---
x-pack/test/fleet_api_integration/apis/agents/upgrade.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts b/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts
index 0e2ebf5c9c9cc..0c31fd5d710cc 100644
--- a/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts
+++ b/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts
@@ -21,8 +21,7 @@ export default function (providerContext: FtrProviderContext) {
const kibanaServer = getService('kibanaServer');
const supertestWithoutAuth = getService('supertestWithoutAuth');
- // Failing: See https://github.com/elastic/kibana/issues/161557
- describe.skip('fleet_upgrade_agent', () => {
+ describe('fleet_upgrade_agent', () => {
skipIfNoDockerRegistry(providerContext);
before(async () => {
await esArchiver.load('x-pack/test/functional/es_archives/fleet/agents');
From 41056193d9357d725cfcbe0b7725854c24cf7aa5 Mon Sep 17 00:00:00 2001
From: Elena Stoeva <59341489+ElenaStoeva@users.noreply.github.com>
Date: Fri, 14 Jul 2023 15:53:17 +0100
Subject: [PATCH 24/50] [Serverless] Add internal uiSettings routes and expose
public routes in self-managed only (#160499)
Partially addresses https://github.com/elastic/kibana/issues/159590
## Summary
This PR adds an an internal uiSettings API that is a duplicate of the
public API and is intended for use by the browser-side uiSettings
client.
The PR also adds a config settings that is configured in serverless
context only and exposes the public uiSettings routes based on the value
of this setting (it defaults to false since we don't want to expose the
public routes in serverless).
**How to test:**
I. Verify that in serverless the internal routes are exposed but the
public ones aren't:
1. Start Es with `yarn es snapshot` and Kibana with `yarn
serverless-{mode}` where `{mode}` can be `es`, `oblt`, or `security`
(the public routes should be disabled for all projects).
2. Verify that the public endpoints are not accessible. For example,
`curl --user elastic:changeme
'http://localhost:5601/zhb/api/kibana/settings' -X 'GET'` should return
`{"statusCode":404,"error":"Not Found","message":"Not Found"}`.
3. Verify that the internal endpoints are accessible. For example, `curl
--user elastic:changeme
'http://localhost:5601/zhb/internal/kibana/settings' -X 'GET'` should
return
`{"settings":{"buildNum":{"userValue":9007199254740991},"isDefaultIndexMigrated":{"userValue":true},"defaultRoute":{"isOverridden":true,"userValue":"/app/elasticsearch"}}}`
II. Verify that the both public and internal routes are exposed in
self-managed:
1. Start Es with `yarn es snapshot` and Kibana with `yarn start`
2. Verify that the public endpoints are accessible. For example, `curl
--user elastic:changeme 'http://localhost:5601/zhb/api/kibana/settings'
-X 'GET'` should return
`{"settings":{"buildNum":{"userValue":9007199254740991},"isDefaultIndexMigrated":{"userValue":true}}}`
3. Verify that the internal endpoints are accessible. For example, `curl
--user elastic:changeme
'http://localhost:5601/zhb/internal/kibana/settings' -X 'GET'` should
return
`{"settings":{"buildNum":{"userValue":9007199254740991},"isDefaultIndexMigrated":{"userValue":true}}}`
III. Verify that the plugins/services that consume the internal
uiSettings endpoints work as expected in both self-managed and
serverless environment.
### For maintainers
- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
---
.../ui_settings_api.test.ts.snap | 28 +++---
.../src/ui_settings_api.ts | 3 +-
.../src/routes/index.ts | 2 +
.../src/routes/internal/delete.ts | 68 ++++++++++++++
.../src/routes/internal/get.ts | 53 +++++++++++
.../src/routes/internal/index.ts | 20 +++++
.../src/routes/internal/set.ts | 80 +++++++++++++++++
.../src/routes/internal/set_many.ts | 71 +++++++++++++++
.../src/ui_settings_config.ts | 6 ++
.../src/ui_settings_service.ts | 14 ++-
.../src/kbn_client/kbn_client_ui_settings.ts | 8 +-
.../default_route_provider_config.test.ts | 4 +-
.../ui_settings/doc_exists.ts | 32 ++++---
.../ui_settings/doc_missing.ts | 10 ++-
.../ui_settings/routes.test.ts | 89 ++++++++++++++++++-
.../apm/ftr_e2e/cypress/support/commands.ts | 2 +-
.../fleet/cypress/tasks/ui_settings.ts | 2 +-
.../api_calls/kibana_advanced_settings.ts | 2 +-
.../cypress/tasks/timeline.ts | 2 +-
.../advanced_settings/feature_controls.ts | 2 +-
.../reporting_and_security/spaces.ts | 2 +-
21 files changed, 450 insertions(+), 50 deletions(-)
create mode 100644 packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/delete.ts
create mode 100644 packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/get.ts
create mode 100644 packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/index.ts
create mode 100644 packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/set.ts
create mode 100644 packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/set_many.ts
diff --git a/packages/core/ui-settings/core-ui-settings-browser-internal/src/__snapshots__/ui_settings_api.test.ts.snap b/packages/core/ui-settings/core-ui-settings-browser-internal/src/__snapshots__/ui_settings_api.test.ts.snap
index abee12da71e6b..b80765e3536c9 100644
--- a/packages/core/ui-settings/core-ui-settings-browser-internal/src/__snapshots__/ui_settings_api.test.ts.snap
+++ b/packages/core/ui-settings/core-ui-settings-browser-internal/src/__snapshots__/ui_settings_api.test.ts.snap
@@ -3,7 +3,7 @@
exports[`#batchSet Buffers are always clear of previously buffered changes: two requests, second only sends bar, not foo 1`] = `
Array [
Array [
- "/foo/bar/api/kibana/settings",
+ "/foo/bar/internal/kibana/settings",
Object {
"headers": Object {
"accept": "application/json",
@@ -15,7 +15,7 @@ Array [
},
],
Array [
- "/foo/bar/api/kibana/settings",
+ "/foo/bar/internal/kibana/settings",
Object {
"headers": Object {
"accept": "application/json",
@@ -32,7 +32,7 @@ Array [
exports[`#batchSet Overwrites previously buffered values with new values for the same key: two requests, foo=d in final 1`] = `
Array [
Array [
- "/foo/bar/api/kibana/settings",
+ "/foo/bar/internal/kibana/settings",
Object {
"headers": Object {
"accept": "application/json",
@@ -44,7 +44,7 @@ Array [
},
],
Array [
- "/foo/bar/api/kibana/settings",
+ "/foo/bar/internal/kibana/settings",
Object {
"headers": Object {
"accept": "application/json",
@@ -61,7 +61,7 @@ Array [
exports[`#batchSet buffers changes while first request is in progress, sends buffered changes after first request completes: final, includes both requests 1`] = `
Array [
Array [
- "/foo/bar/api/kibana/settings",
+ "/foo/bar/internal/kibana/settings",
Object {
"headers": Object {
"accept": "application/json",
@@ -73,7 +73,7 @@ Array [
},
],
Array [
- "/foo/bar/api/kibana/settings",
+ "/foo/bar/internal/kibana/settings",
Object {
"headers": Object {
"accept": "application/json",
@@ -113,7 +113,7 @@ exports[`#batchSet rejects on 500 1`] = `"Request failed with status code: 500"`
exports[`#batchSet sends a single change immediately: single change 1`] = `
Array [
Array [
- "/foo/bar/api/kibana/settings",
+ "/foo/bar/internal/kibana/settings",
Object {
"headers": Object {
"accept": "application/json",
@@ -130,7 +130,7 @@ Array [
exports[`#batchSetGlobal Buffers are always clear of previously buffered changes: two requests, second only sends bar, not foo 1`] = `
Array [
Array [
- "/foo/bar/api/kibana/global_settings",
+ "/foo/bar/internal/kibana/global_settings",
Object {
"headers": Object {
"accept": "application/json",
@@ -142,7 +142,7 @@ Array [
},
],
Array [
- "/foo/bar/api/kibana/global_settings",
+ "/foo/bar/internal/kibana/global_settings",
Object {
"headers": Object {
"accept": "application/json",
@@ -159,7 +159,7 @@ Array [
exports[`#batchSetGlobal Overwrites previously buffered values with new values for the same key: two requests, foo=d in final 1`] = `
Array [
Array [
- "/foo/bar/api/kibana/global_settings",
+ "/foo/bar/internal/kibana/global_settings",
Object {
"headers": Object {
"accept": "application/json",
@@ -171,7 +171,7 @@ Array [
},
],
Array [
- "/foo/bar/api/kibana/global_settings",
+ "/foo/bar/internal/kibana/global_settings",
Object {
"headers": Object {
"accept": "application/json",
@@ -188,7 +188,7 @@ Array [
exports[`#batchSetGlobal buffers changes while first request is in progress, sends buffered changes after first request completes: final, includes both requests 1`] = `
Array [
Array [
- "/foo/bar/api/kibana/global_settings",
+ "/foo/bar/internal/kibana/global_settings",
Object {
"headers": Object {
"accept": "application/json",
@@ -200,7 +200,7 @@ Array [
},
],
Array [
- "/foo/bar/api/kibana/global_settings",
+ "/foo/bar/internal/kibana/global_settings",
Object {
"headers": Object {
"accept": "application/json",
@@ -240,7 +240,7 @@ exports[`#batchSetGlobal rejects on 500 1`] = `"Request failed with status code:
exports[`#batchSetGlobal sends a single change immediately: single change 1`] = `
Array [
Array [
- "/foo/bar/api/kibana/global_settings",
+ "/foo/bar/internal/kibana/global_settings",
Object {
"headers": Object {
"accept": "application/json",
diff --git a/packages/core/ui-settings/core-ui-settings-browser-internal/src/ui_settings_api.ts b/packages/core/ui-settings/core-ui-settings-browser-internal/src/ui_settings_api.ts
index 98141eb1163b5..c96232b9f9b48 100644
--- a/packages/core/ui-settings/core-ui-settings-browser-internal/src/ui_settings_api.ts
+++ b/packages/core/ui-settings/core-ui-settings-browser-internal/src/ui_settings_api.ts
@@ -137,7 +137,8 @@ export class UiSettingsApi {
try {
this.sendInProgress = true;
- const path = scope === 'namespace' ? '/api/kibana/settings' : '/api/kibana/global_settings';
+ const path =
+ scope === 'namespace' ? '/internal/kibana/settings' : '/internal/kibana/global_settings';
changes.callback(
undefined,
await this.sendRequest('POST', path, {
diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/index.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/index.ts
index 22ca2ae38cde5..1fa1afa333e23 100644
--- a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/index.ts
+++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/index.ts
@@ -18,3 +18,5 @@ export function registerRoutes(router: InternalUiSettingsRouter) {
registerSetRoute(router);
registerSetManyRoute(router);
}
+
+export { registerInternalRoutes } from './internal';
diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/delete.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/delete.ts
new file mode 100644
index 0000000000000..91a81a252edd2
--- /dev/null
+++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/delete.ts
@@ -0,0 +1,68 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 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 { schema } from '@kbn/config-schema';
+
+import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server';
+import { IUiSettingsClient } from '@kbn/core-ui-settings-server';
+import { KibanaRequest, KibanaResponseFactory } from '@kbn/core-http-server';
+import type { InternalUiSettingsRouter } from '../../internal_types';
+import { CannotOverrideError } from '../../ui_settings_errors';
+import { InternalUiSettingsRequestHandlerContext } from '../../internal_types';
+
+const validate = {
+ params: schema.object({
+ key: schema.string(),
+ }),
+};
+
+export function registerInternalDeleteRoute(router: InternalUiSettingsRouter) {
+ const deleteFromRequest = async (
+ uiSettingsClient: IUiSettingsClient,
+ context: InternalUiSettingsRequestHandlerContext,
+ request: KibanaRequest, unknown, unknown, 'delete'>,
+ response: KibanaResponseFactory
+ ) => {
+ try {
+ await uiSettingsClient.remove(request.params.key);
+
+ return response.ok({
+ body: {
+ settings: await uiSettingsClient.getUserProvided(),
+ },
+ });
+ } catch (error) {
+ if (SavedObjectsErrorHelpers.isSavedObjectsClientError(error)) {
+ return response.customError({
+ body: error,
+ statusCode: error.output.statusCode,
+ });
+ }
+
+ if (error instanceof CannotOverrideError) {
+ return response.badRequest({ body: error });
+ }
+
+ throw error;
+ }
+ };
+ router.delete(
+ { path: '/internal/kibana/settings/{key}', validate, options: { access: 'internal' } },
+ async (context, request, response) => {
+ const uiSettingsClient = (await context.core).uiSettings.client;
+ return await deleteFromRequest(uiSettingsClient, context, request, response);
+ }
+ );
+ router.delete(
+ { path: '/internal/kibana/global_settings/{key}', validate, options: { access: 'internal' } },
+ async (context, request, response) => {
+ const uiSettingsClient = (await context.core).uiSettings.globalClient;
+ return await deleteFromRequest(uiSettingsClient, context, request, response);
+ }
+ );
+}
diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/get.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/get.ts
new file mode 100644
index 0000000000000..f9323f45e786d
--- /dev/null
+++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/get.ts
@@ -0,0 +1,53 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 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 { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server';
+import { IUiSettingsClient } from '@kbn/core-ui-settings-server';
+import { KibanaRequest, KibanaResponseFactory } from '@kbn/core-http-server';
+import { InternalUiSettingsRequestHandlerContext } from '../../internal_types';
+import type { InternalUiSettingsRouter } from '../../internal_types';
+
+export function registerInternalGetRoute(router: InternalUiSettingsRouter) {
+ const getFromRequest = async (
+ uiSettingsClient: IUiSettingsClient,
+ context: InternalUiSettingsRequestHandlerContext,
+ request: KibanaRequest,
+ response: KibanaResponseFactory
+ ) => {
+ try {
+ return response.ok({
+ body: {
+ settings: await uiSettingsClient.getUserProvided(),
+ },
+ });
+ } catch (error) {
+ if (SavedObjectsErrorHelpers.isSavedObjectsClientError(error)) {
+ return response.customError({
+ body: error,
+ statusCode: error.output.statusCode,
+ });
+ }
+
+ throw error;
+ }
+ };
+ router.get(
+ { path: '/internal/kibana/settings', validate: false, options: { access: 'internal' } },
+ async (context, request, response) => {
+ const uiSettingsClient = (await context.core).uiSettings.client;
+ return await getFromRequest(uiSettingsClient, context, request, response);
+ }
+ );
+ router.get(
+ { path: '/internal/kibana/global_settings', validate: false, options: { access: 'internal' } },
+ async (context, request, response) => {
+ const uiSettingsClient = (await context.core).uiSettings.globalClient;
+ return await getFromRequest(uiSettingsClient, context, request, response);
+ }
+ );
+}
diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/index.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/index.ts
new file mode 100644
index 0000000000000..e15ded76ab6b8
--- /dev/null
+++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/index.ts
@@ -0,0 +1,20 @@
+/*
+ * 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 type { InternalUiSettingsRouter } from '../../internal_types';
+import { registerInternalDeleteRoute } from './delete';
+import { registerInternalGetRoute } from './get';
+import { registerInternalSetManyRoute } from './set_many';
+import { registerInternalSetRoute } from './set';
+
+export function registerInternalRoutes(router: InternalUiSettingsRouter) {
+ registerInternalGetRoute(router);
+ registerInternalDeleteRoute(router);
+ registerInternalSetRoute(router);
+ registerInternalSetManyRoute(router);
+}
diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/set.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/set.ts
new file mode 100644
index 0000000000000..a7bc0f8589599
--- /dev/null
+++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/set.ts
@@ -0,0 +1,80 @@
+/*
+ * 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 { schema, ValidationError } from '@kbn/config-schema';
+import { KibanaRequest, KibanaResponseFactory } from '@kbn/core-http-server';
+import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server';
+import { IUiSettingsClient } from '@kbn/core-ui-settings-server';
+import type {
+ InternalUiSettingsRequestHandlerContext,
+ InternalUiSettingsRouter,
+} from '../../internal_types';
+import { CannotOverrideError } from '../../ui_settings_errors';
+
+const validate = {
+ params: schema.object({
+ key: schema.string(),
+ }),
+ body: schema.object({
+ value: schema.any(),
+ }),
+};
+
+export function registerInternalSetRoute(router: InternalUiSettingsRouter) {
+ const setFromRequest = async (
+ uiSettingsClient: IUiSettingsClient,
+ context: InternalUiSettingsRequestHandlerContext,
+ request: KibanaRequest<
+ Readonly<{} & { key: string }>,
+ unknown,
+ Readonly<{ value?: any } & {}>,
+ 'post'
+ >,
+ response: KibanaResponseFactory
+ ) => {
+ try {
+ const { key } = request.params;
+ const { value } = request.body;
+
+ await uiSettingsClient.set(key, value);
+
+ return response.ok({
+ body: {
+ settings: await uiSettingsClient.getUserProvided(),
+ },
+ });
+ } catch (error) {
+ if (SavedObjectsErrorHelpers.isSavedObjectsClientError(error)) {
+ return response.customError({
+ body: error,
+ statusCode: error.output.statusCode,
+ });
+ }
+
+ if (error instanceof CannotOverrideError || error instanceof ValidationError) {
+ return response.badRequest({ body: error });
+ }
+
+ throw error;
+ }
+ };
+ router.post(
+ { path: '/internal/kibana/settings/{key}', validate, options: { access: 'internal' } },
+ async (context, request, response) => {
+ const uiSettingsClient = (await context.core).uiSettings.client;
+ return await setFromRequest(uiSettingsClient, context, request, response);
+ }
+ );
+ router.post(
+ { path: '/internal/kibana/global_settings/{key}', validate, options: { access: 'internal' } },
+ async (context, request, response) => {
+ const uiSettingsClient = (await context.core).uiSettings.globalClient;
+ return await setFromRequest(uiSettingsClient, context, request, response);
+ }
+ );
+}
diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/set_many.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/set_many.ts
new file mode 100644
index 0000000000000..155f9c81120db
--- /dev/null
+++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/set_many.ts
@@ -0,0 +1,71 @@
+/*
+ * 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 { schema, ValidationError } from '@kbn/config-schema';
+import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server';
+import { KibanaRequest, KibanaResponseFactory } from '@kbn/core-http-server';
+import { IUiSettingsClient } from '@kbn/core-ui-settings-server';
+import type { InternalUiSettingsRouter } from '../../internal_types';
+import { CannotOverrideError } from '../../ui_settings_errors';
+import { InternalUiSettingsRequestHandlerContext } from '../../internal_types';
+
+const validate = {
+ body: schema.object({
+ changes: schema.object({}, { unknowns: 'allow' }),
+ }),
+};
+
+export function registerInternalSetManyRoute(router: InternalUiSettingsRouter) {
+ const setManyFromRequest = async (
+ uiSettingsClient: IUiSettingsClient,
+ context: InternalUiSettingsRequestHandlerContext,
+ request: KibanaRequest, 'post'>,
+ response: KibanaResponseFactory
+ ) => {
+ try {
+ const { changes } = request.body;
+
+ await uiSettingsClient.setMany(changes);
+
+ return response.ok({
+ body: {
+ settings: await uiSettingsClient.getUserProvided(),
+ },
+ });
+ } catch (error) {
+ if (SavedObjectsErrorHelpers.isSavedObjectsClientError(error)) {
+ return response.customError({
+ body: error,
+ statusCode: error.output.statusCode,
+ });
+ }
+
+ if (error instanceof CannotOverrideError || error instanceof ValidationError) {
+ return response.badRequest({ body: error });
+ }
+
+ throw error;
+ }
+ };
+
+ router.post(
+ { path: '/internal/kibana/settings', validate, options: { access: 'internal' } },
+ async (context, request, response) => {
+ const uiSettingsClient = (await context.core).uiSettings.client;
+ return await setManyFromRequest(uiSettingsClient, context, request, response);
+ }
+ );
+
+ router.post(
+ { path: '/internal/kibana/global_settings', validate, options: { access: 'internal' } },
+ async (context, request, response) => {
+ const uiSettingsClient = (await context.core).uiSettings.globalClient;
+ return await setManyFromRequest(uiSettingsClient, context, request, response);
+ }
+ );
+}
diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/ui_settings_config.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/ui_settings_config.ts
index 5e5d5a05345ba..bfcc5316a78a0 100644
--- a/packages/core/ui-settings/core-ui-settings-server-internal/src/ui_settings_config.ts
+++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/ui_settings_config.ts
@@ -17,6 +17,12 @@ const deprecations: ConfigDeprecationProvider = ({ unused, renameFromRoot }) =>
const configSchema = schema.object({
overrides: schema.object({}, { unknowns: 'allow' }),
+ publicApiEnabled: schema.conditional(
+ schema.contextRef('serverless'),
+ true,
+ schema.boolean({ defaultValue: false }),
+ schema.never()
+ ),
});
export type UiSettingsConfigType = TypeOf;
diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/ui_settings_service.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/ui_settings_service.ts
index 473e7569e2179..3352ab0ab63b0 100644
--- a/packages/core/ui-settings/core-ui-settings-server-internal/src/ui_settings_service.ts
+++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/ui_settings_service.ts
@@ -25,7 +25,7 @@ import type {
} from './types';
import type { InternalUiSettingsRequestHandlerContext } from './internal_types';
import { uiSettingsType, uiSettingsGlobalType } from './saved_objects';
-import { registerRoutes } from './routes';
+import { registerRoutes, registerInternalRoutes } from './routes';
import { getCoreSettings } from './settings';
import { UiSettingsDefaultsClient } from './clients/ui_settings_defaults_client';
@@ -78,12 +78,18 @@ export class UiSettingsService
public async setup({ http, savedObjects }: SetupDeps): Promise {
this.log.debug('Setting up ui settings service');
+ const config = await firstValueFrom(this.config$);
+ this.overrides = config.overrides;
savedObjects.registerType(uiSettingsType);
savedObjects.registerType(uiSettingsGlobalType);
- registerRoutes(http.createRouter(''));
- const config = await firstValueFrom(this.config$);
- this.overrides = config.overrides;
+ const router = http.createRouter('');
+ registerInternalRoutes(router);
+
+ // Register public routes by default unless the publicApiEnabled config setting is set to false
+ if (!config.hasOwnProperty('publicApiEnabled') || config.publicApiEnabled === true) {
+ registerRoutes(router);
+ }
return {
register: this.register,
diff --git a/packages/kbn-test/src/kbn_client/kbn_client_ui_settings.ts b/packages/kbn-test/src/kbn_client/kbn_client_ui_settings.ts
index 7be72e7b484ba..8b9277fdab8b9 100644
--- a/packages/kbn-test/src/kbn_client/kbn_client_ui_settings.ts
+++ b/packages/kbn-test/src/kbn_client/kbn_client_ui_settings.ts
@@ -47,7 +47,7 @@ export class KbnClientUiSettings {
*/
async unset(setting: string, { space }: { space?: string } = {}) {
const { data } = await this.requester.request({
- path: pathWithSpace(space)`/api/kibana/settings/${setting}`,
+ path: pathWithSpace(space)`/internal/kibana/settings/${setting}`,
method: 'DELETE',
});
return data;
@@ -76,7 +76,7 @@ export class KbnClientUiSettings {
await this.requester.request({
method: 'POST',
- path: pathWithSpace(space)`/api/kibana/settings`,
+ path: pathWithSpace(space)`/internal/kibana/settings`,
body: { changes },
retries,
});
@@ -89,7 +89,7 @@ export class KbnClientUiSettings {
this.log.debug('applying update to kibana config: %j', updates);
await this.requester.request({
- path: pathWithSpace(space)`/api/kibana/settings`,
+ path: pathWithSpace(space)`/internal/kibana/settings`,
method: 'POST',
body: {
changes: updates,
@@ -100,7 +100,7 @@ export class KbnClientUiSettings {
private async getAll({ space }: { space?: string } = {}) {
const { data } = await this.requester.request({
- path: pathWithSpace(space)`/api/kibana/settings`,
+ path: pathWithSpace(space)`/internal/kibana/settings`,
method: 'GET',
});
diff --git a/src/core/server/integration_tests/core_app/default_route_provider_config.test.ts b/src/core/server/integration_tests/core_app/default_route_provider_config.test.ts
index 2b6faff61bdf6..c3e595dc20958 100644
--- a/src/core/server/integration_tests/core_app/default_route_provider_config.test.ts
+++ b/src/core/server/integration_tests/core_app/default_route_provider_config.test.ts
@@ -58,7 +58,7 @@ describe('default route provider', () => {
for (const url of invalidRoutes) {
await request
- .post(root, '/api/kibana/settings/defaultRoute')
+ .post(root, '/internal/kibana/settings/defaultRoute')
.send({ value: url })
.expect(400);
}
@@ -72,7 +72,7 @@ describe('default route provider', () => {
it('consumes valid values', async function () {
await request
- .post(root, '/api/kibana/settings/defaultRoute')
+ .post(root, '/internal/kibana/settings/defaultRoute')
.send({ value: '/valid' })
.expect(200);
diff --git a/src/core/server/integration_tests/ui_settings/doc_exists.ts b/src/core/server/integration_tests/ui_settings/doc_exists.ts
index 0f4191e92ec2e..2247fd9ed68aa 100644
--- a/src/core/server/integration_tests/ui_settings/doc_exists.ts
+++ b/src/core/server/integration_tests/ui_settings/doc_exists.ts
@@ -50,7 +50,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
},
});
- const { body } = await supertest('get', '/api/kibana/settings').expect(200);
+ const { body } = await supertest('get', '/internal/kibana/settings').expect(200);
expect(body).toMatchObject({
settings: {
@@ -75,7 +75,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
const defaultIndex = chance.word();
- const { body } = await supertest('post', '/api/kibana/settings/defaultIndex')
+ const { body } = await supertest('post', '/internal/kibana/settings/defaultIndex')
.send({
value: defaultIndex,
})
@@ -100,7 +100,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
it('returns a 400 if trying to set overridden value', async () => {
const { supertest } = await setup();
- const { body } = await supertest('delete', '/api/kibana/settings/foo')
+ const { body } = await supertest('delete', '/internal/kibana/settings/foo')
.send({
value: 'baz',
})
@@ -119,7 +119,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
const { supertest } = await setup();
const defaultIndex = chance.word();
- const { body } = await supertest('post', '/api/kibana/settings')
+ const { body } = await supertest('post', '/internal/kibana/settings')
.send({
changes: {
defaultIndex,
@@ -146,7 +146,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
it('returns a 400 if trying to set overridden value', async () => {
const { supertest } = await setup();
- const { body } = await supertest('post', '/api/kibana/settings')
+ const { body } = await supertest('post', '/internal/kibana/settings')
.send({
changes: {
foo: 'baz',
@@ -172,7 +172,9 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
expect(await uiSettings.get('defaultIndex')).toBe(defaultIndex);
- const { body } = await supertest('delete', '/api/kibana/settings/defaultIndex').expect(200);
+ const { body } = await supertest('delete', '/internal/kibana/settings/defaultIndex').expect(
+ 200
+ );
expect(body).toMatchObject({
settings: {
@@ -189,7 +191,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
it('returns a 400 if deleting overridden value', async () => {
const { supertest } = await setup();
- const { body } = await supertest('delete', '/api/kibana/settings/foo').expect(400);
+ const { body } = await supertest('delete', '/internal/kibana/settings/foo').expect(400);
expect(body).toEqual({
error: 'Bad Request',
@@ -210,7 +212,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
},
});
- const { body } = await supertest('get', '/api/kibana/global_settings').expect(200);
+ const { body } = await supertest('get', '/internal/kibana/global_settings').expect(200);
expect(body).toMatchObject({
settings: {
@@ -231,7 +233,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
const defaultIndex = chance.word();
- const { body } = await supertest('post', '/api/kibana/global_settings/defaultIndex')
+ const { body } = await supertest('post', '/internal/kibana/global_settings/defaultIndex')
.send({
value: defaultIndex,
})
@@ -253,7 +255,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
it.skip('returns a 400 if trying to set overridden value', async () => {
const { supertest } = await setup();
- const { body } = await supertest('delete', '/api/kibana/global_settings/foo')
+ const { body } = await supertest('delete', '/internal/kibana/global_settings/foo')
.send({
value: 'baz',
})
@@ -272,7 +274,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
const { supertest } = await setup();
const defaultIndex = chance.word();
- const { body } = await supertest('post', '/api/kibana/global_settings')
+ const { body } = await supertest('post', '/internal/kibana/global_settings')
.send({
changes: {
defaultIndex,
@@ -296,7 +298,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
it.skip('returns a 400 if trying to set overridden value', async () => {
const { supertest } = await setup();
- const { body } = await supertest('post', '/api/kibana/global_settings')
+ const { body } = await supertest('post', '/internal/kibana/global_settings')
.send({
changes: {
foo: 'baz',
@@ -324,7 +326,7 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
const { body } = await supertest(
'delete',
- '/api/kibana/global_settings/defaultIndex'
+ '/internal/kibana/global_settings/defaultIndex'
).expect(200);
expect(body).toMatchObject({
@@ -339,7 +341,9 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
it.skip('returns a 400 if deleting overridden value', async () => {
const { supertest } = await setup();
- const { body } = await supertest('delete', '/api/kibana/global_settings/foo').expect(400);
+ const { body } = await supertest('delete', '/internal/kibana/global_settings/foo').expect(
+ 400
+ );
expect(body).toEqual({
error: 'Bad Request',
diff --git a/src/core/server/integration_tests/ui_settings/doc_missing.ts b/src/core/server/integration_tests/ui_settings/doc_missing.ts
index f48024ff6c928..b31514d4d11c8 100644
--- a/src/core/server/integration_tests/ui_settings/doc_missing.ts
+++ b/src/core/server/integration_tests/ui_settings/doc_missing.ts
@@ -26,7 +26,7 @@ export const docMissingSuite = (savedObjectsIndex: string) => () => {
it('creates doc, returns a 200 with settings', async () => {
const { supertest } = getServices();
- const { body } = await supertest('get', '/api/kibana/settings').expect(200);
+ const { body } = await supertest('get', '/internal/kibana/settings').expect(200);
expect(body).toMatchObject({
settings: {
@@ -47,7 +47,7 @@ export const docMissingSuite = (savedObjectsIndex: string) => () => {
const { supertest } = getServices();
const defaultIndex = chance.word();
- const { body } = await supertest('post', '/api/kibana/settings/defaultIndex')
+ const { body } = await supertest('post', '/internal/kibana/settings/defaultIndex')
.send({
value: defaultIndex,
})
@@ -76,7 +76,7 @@ export const docMissingSuite = (savedObjectsIndex: string) => () => {
const defaultIndex = chance.word();
- const { body } = await supertest('post', '/api/kibana/settings')
+ const { body } = await supertest('post', '/internal/kibana/settings')
.send({
changes: { defaultIndex },
})
@@ -103,7 +103,9 @@ export const docMissingSuite = (savedObjectsIndex: string) => () => {
it('creates doc, returns a 200 with just buildNum', async () => {
const { supertest } = getServices();
- const { body } = await supertest('delete', '/api/kibana/settings/defaultIndex').expect(200);
+ const { body } = await supertest('delete', '/internal/kibana/settings/defaultIndex').expect(
+ 200
+ );
expect(body).toMatchObject({
settings: {
diff --git a/src/core/server/integration_tests/ui_settings/routes.test.ts b/src/core/server/integration_tests/ui_settings/routes.test.ts
index 8ca1a1ad7dad1..6999731947d86 100644
--- a/src/core/server/integration_tests/ui_settings/routes.test.ts
+++ b/src/core/server/integration_tests/ui_settings/routes.test.ts
@@ -10,7 +10,7 @@ import { schema } from '@kbn/config-schema';
import { createRoot, request } from '@kbn/core-test-helpers-kbn-server';
describe('ui settings service', () => {
- describe('routes', () => {
+ describe('public routes', () => {
let root: ReturnType;
beforeAll(async () => {
root = createRoot({
@@ -96,4 +96,91 @@ describe('ui settings service', () => {
});
});
});
+
+ describe('internal routes', () => {
+ let root: ReturnType;
+ beforeAll(async () => {
+ root = createRoot({
+ plugins: { initialize: false },
+ elasticsearch: { skipStartupConnectionCheck: true },
+ });
+
+ await root.preboot();
+ const { uiSettings } = await root.setup();
+ uiSettings.register({
+ custom: {
+ value: '42',
+ schema: schema.string(),
+ },
+ });
+ // global uiSettings have to be registerd to be set
+ uiSettings.registerGlobal({
+ custom: {
+ value: '42',
+ schema: schema.string(),
+ },
+ });
+ uiSettings.registerGlobal({
+ foo: {
+ value: 'foo',
+ schema: schema.string(),
+ },
+ });
+
+ await root.start();
+ });
+ afterAll(async () => await root.shutdown());
+
+ describe('set', () => {
+ it('validates value', async () => {
+ const response = await request
+ .post(root, '/internal/kibana/settings/custom')
+ .send({ value: 100 })
+ .expect(400);
+
+ expect(response.body.message).toBe(
+ '[validation [custom]]: expected value of type [string] but got [number]'
+ );
+ });
+ });
+ describe('set many', () => {
+ it('validates value', async () => {
+ const response = await request
+ .post(root, '/internal/kibana/settings')
+ .send({ changes: { custom: 100, foo: 'bar' } })
+ .expect(400);
+
+ expect(response.body.message).toBe(
+ '[validation [custom]]: expected value of type [string] but got [number]'
+ );
+ });
+ });
+
+ describe('global', () => {
+ describe('set', () => {
+ it('validates value', async () => {
+ const response = await request
+ .post(root, '/internal/kibana/global_settings/custom')
+ .send({ value: 100 })
+ .expect(400);
+
+ expect(response.body.message).toBe(
+ '[validation [custom]]: expected value of type [string] but got [number]'
+ );
+ });
+ });
+ describe('set many', () => {
+ it('validates value', async () => {
+ const response = await request
+ .post(root, '/internal/kibana/global_settings')
+ .send({ changes: { custom: 100, foo: 'bar' } })
+ .expect(400);
+
+ expect(response.body.message).toBe(
+ '[validation [custom]]: expected value of type [string] but got [number]'
+ );
+ });
+ });
+ });
+ });
});
diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts b/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts
index 22f9090049a60..c0b9f84797f87 100644
--- a/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts
+++ b/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts
@@ -122,7 +122,7 @@ Cypress.Commands.add(
cy.request({
log: false,
method: 'POST',
- url: `${kibanaUrl}/api/kibana/settings`,
+ url: `${kibanaUrl}/internal/kibana/settings`,
body: { changes: settings },
headers: {
'kbn-xsrf': 'e2e_test',
diff --git a/x-pack/plugins/fleet/cypress/tasks/ui_settings.ts b/x-pack/plugins/fleet/cypress/tasks/ui_settings.ts
index 4ba239e107f1b..fceffad66c41e 100644
--- a/x-pack/plugins/fleet/cypress/tasks/ui_settings.ts
+++ b/x-pack/plugins/fleet/cypress/tasks/ui_settings.ts
@@ -9,7 +9,7 @@
export function setUISettings(settingsKey: string, settingsValue: any) {
cy.request({
method: 'POST',
- url: '/api/kibana/settings',
+ url: '/internal/kibana/settings',
headers: { 'kbn-xsrf': 'xx' },
body: {
changes: {
diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/kibana_advanced_settings.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/kibana_advanced_settings.ts
index 859df059f742d..0cadb50d72fe1 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/kibana_advanced_settings.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/kibana_advanced_settings.ts
@@ -10,7 +10,7 @@ import { rootRequest } from '../common';
const kibanaSettings = (body: Cypress.RequestBody) => {
rootRequest({
method: 'POST',
- url: 'api/kibana/settings',
+ url: 'internal/kibana/settings',
body,
headers: { 'kbn-xsrf': 'cypress-creds' },
});
diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts
index d42d3ecfede31..05d08ba9702bf 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts
@@ -471,7 +471,7 @@ export const setKibanaTimezoneToUTC = () =>
cy
.request({
method: 'POST',
- url: 'api/kibana/settings',
+ url: 'internal/kibana/settings',
body: { changes: { 'dateFormat:tz': 'UTC' } },
headers: { 'kbn-xsrf': 'set-kibana-timezone-utc' },
})
diff --git a/x-pack/test/api_integration/apis/management/advanced_settings/feature_controls.ts b/x-pack/test/api_integration/apis/management/advanced_settings/feature_controls.ts
index a17f22c245a4c..ccc9b92ac8298 100644
--- a/x-pack/test/api_integration/apis/management/advanced_settings/feature_controls.ts
+++ b/x-pack/test/api_integration/apis/management/advanced_settings/feature_controls.ts
@@ -52,7 +52,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext)
const basePath = spaceId ? `/s/${spaceId}` : '';
return await supertest
- .post(`${basePath}/api/kibana/settings`)
+ .post(`${basePath}/internal/kibana/settings`)
.auth(username, password)
.set('kbn-xsrf', 'foo')
.send({ changes: { [CSV_QUOTE_VALUES_SETTING]: null } })
diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/spaces.ts b/x-pack/test/reporting_api_integration/reporting_and_security/spaces.ts
index 5bce4f5b8243a..12d85ec4f9076 100644
--- a/x-pack/test/reporting_api_integration/reporting_and_security/spaces.ts
+++ b/x-pack/test/reporting_api_integration/reporting_and_security/spaces.ts
@@ -21,7 +21,7 @@ export default function ({ getService }: FtrProviderContext) {
const setSpaceConfig = async (spaceId: string, settings: object) => {
return await kibanaServer.request({
- path: `/s/${spaceId}/api/kibana/settings`,
+ path: `/s/${spaceId}/internal/kibana/settings`,
method: 'POST',
body: { changes: settings },
});
From 235eac899b3b64a767c2f900df49be0e7ddeeb5e Mon Sep 17 00:00:00 2001
From: Nathan Reese
Date: Fri, 14 Jul 2023 09:11:56 -0600
Subject: [PATCH 25/50] [maps] fix Data Mapper resets after switch to clusters
for scaling (#161796)
Closes https://github.com/elastic/kibana/issues/158551
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
---
.../classes/fields/agg/agg_field.test.ts | 110 +++++++++++++++---
.../public/classes/fields/agg/agg_field.ts | 2 +-
.../properties/dynamic_style_property.test.ts | 61 +++++++++-
.../properties/dynamic_style_property.tsx | 11 +-
4 files changed, 158 insertions(+), 26 deletions(-)
diff --git a/x-pack/plugins/maps/public/classes/fields/agg/agg_field.test.ts b/x-pack/plugins/maps/public/classes/fields/agg/agg_field.test.ts
index d8346fa8f5283..ac2999246da6f 100644
--- a/x-pack/plugins/maps/public/classes/fields/agg/agg_field.test.ts
+++ b/x-pack/plugins/maps/public/classes/fields/agg/agg_field.test.ts
@@ -9,11 +9,9 @@ import { AggField } from './agg_field';
import { AGG_TYPE, FIELD_ORIGIN } from '../../../../common/constants';
import { IESAggSource } from '../../sources/es_agg_source';
-const mockEsAggSource = {} as unknown as IESAggSource;
-
const defaultParams = {
label: 'my agg field',
- source: mockEsAggSource,
+ source: {} as unknown as IESAggSource,
origin: FIELD_ORIGIN.SOURCE,
};
@@ -41,24 +39,98 @@ describe('supportsFieldMetaFromEs', () => {
});
describe('supportsFieldMetaFromLocalData', () => {
- test('number metrics should support field meta from local', () => {
- const avgMetric = new AggField({ ...defaultParams, aggType: AGG_TYPE.AVG });
- expect(avgMetric.supportsFieldMetaFromLocalData()).toBe(true);
- const maxMetric = new AggField({ ...defaultParams, aggType: AGG_TYPE.MAX });
- expect(maxMetric.supportsFieldMetaFromLocalData()).toBe(true);
- const minMetric = new AggField({ ...defaultParams, aggType: AGG_TYPE.MIN });
- expect(minMetric.supportsFieldMetaFromLocalData()).toBe(true);
- const sumMetric = new AggField({ ...defaultParams, aggType: AGG_TYPE.SUM });
- expect(sumMetric.supportsFieldMetaFromLocalData()).toBe(true);
- const uniqueCountMetric = new AggField({
- ...defaultParams,
- aggType: AGG_TYPE.UNIQUE_COUNT,
+ describe('source is vector tiles', () => {
+ const mvtSource = {
+ isMvt: () => {
+ return true;
+ },
+ } as unknown as IESAggSource;
+ test('number metrics should support field meta from local', () => {
+ const avgMetric = new AggField({
+ ...defaultParams,
+ source: mvtSource,
+ aggType: AGG_TYPE.AVG,
+ });
+ expect(avgMetric.supportsFieldMetaFromLocalData()).toBe(true);
+ const maxMetric = new AggField({
+ ...defaultParams,
+ source: mvtSource,
+ aggType: AGG_TYPE.MAX,
+ });
+ expect(maxMetric.supportsFieldMetaFromLocalData()).toBe(true);
+ const minMetric = new AggField({
+ ...defaultParams,
+ source: mvtSource,
+ aggType: AGG_TYPE.MIN,
+ });
+ expect(minMetric.supportsFieldMetaFromLocalData()).toBe(true);
+ const sumMetric = new AggField({
+ ...defaultParams,
+ source: mvtSource,
+ aggType: AGG_TYPE.SUM,
+ });
+ expect(sumMetric.supportsFieldMetaFromLocalData()).toBe(true);
+ const uniqueCountMetric = new AggField({
+ ...defaultParams,
+ source: mvtSource,
+ aggType: AGG_TYPE.UNIQUE_COUNT,
+ });
+ expect(uniqueCountMetric.supportsFieldMetaFromLocalData()).toBe(true);
+ });
+
+ test('Non number metrics should not support field meta from local', () => {
+ const termMetric = new AggField({
+ ...defaultParams,
+ source: mvtSource,
+ aggType: AGG_TYPE.TERMS,
+ });
+ expect(termMetric.supportsFieldMetaFromLocalData()).toBe(false);
});
- expect(uniqueCountMetric.supportsFieldMetaFromLocalData()).toBe(true);
});
- test('Non number metrics should not support field meta from local', () => {
- const termMetric = new AggField({ ...defaultParams, aggType: AGG_TYPE.TERMS });
- expect(termMetric.supportsFieldMetaFromLocalData()).toBe(false);
+ describe('source is not vector tiles', () => {
+ const geojsonSource = {
+ isMvt: () => {
+ return false;
+ },
+ } as unknown as IESAggSource;
+ test('should always support field meta from local', () => {
+ const avgMetric = new AggField({
+ ...defaultParams,
+ source: geojsonSource,
+ aggType: AGG_TYPE.AVG,
+ });
+ expect(avgMetric.supportsFieldMetaFromLocalData()).toBe(true);
+ const maxMetric = new AggField({
+ ...defaultParams,
+ source: geojsonSource,
+ aggType: AGG_TYPE.MAX,
+ });
+ expect(maxMetric.supportsFieldMetaFromLocalData()).toBe(true);
+ const minMetric = new AggField({
+ ...defaultParams,
+ source: geojsonSource,
+ aggType: AGG_TYPE.MIN,
+ });
+ expect(minMetric.supportsFieldMetaFromLocalData()).toBe(true);
+ const sumMetric = new AggField({
+ ...defaultParams,
+ source: geojsonSource,
+ aggType: AGG_TYPE.SUM,
+ });
+ expect(sumMetric.supportsFieldMetaFromLocalData()).toBe(true);
+ const uniqueCountMetric = new AggField({
+ ...defaultParams,
+ source: geojsonSource,
+ aggType: AGG_TYPE.UNIQUE_COUNT,
+ });
+ expect(uniqueCountMetric.supportsFieldMetaFromLocalData()).toBe(true);
+ const termMetric = new AggField({
+ ...defaultParams,
+ source: geojsonSource,
+ aggType: AGG_TYPE.TERMS,
+ });
+ expect(termMetric.supportsFieldMetaFromLocalData()).toBe(true);
+ });
});
});
diff --git a/x-pack/plugins/maps/public/classes/fields/agg/agg_field.ts b/x-pack/plugins/maps/public/classes/fields/agg/agg_field.ts
index 1a6841ffa9e7e..92136779ac763 100644
--- a/x-pack/plugins/maps/public/classes/fields/agg/agg_field.ts
+++ b/x-pack/plugins/maps/public/classes/fields/agg/agg_field.ts
@@ -44,7 +44,7 @@ export class AggField extends CountAggField {
supportsFieldMetaFromLocalData(): boolean {
// Elasticsearch vector tile search API returns meta tiles with numeric aggregation metrics.
- return this._getDataTypeSynchronous() === 'number';
+ return this._source.isMvt() ? this._getDataTypeSynchronous() === 'number' : true;
}
isValid(): boolean {
diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.test.ts b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.test.ts
index 694b1ac0c2c9a..b684bf5a61de6 100644
--- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.test.ts
+++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.test.ts
@@ -5,7 +5,10 @@
* 2.0.
*/
-import { percentilesValuesToFieldMeta } from './dynamic_style_property';
+import { DynamicStyleProperty, percentilesValuesToFieldMeta } from './dynamic_style_property';
+import type { IField } from '../../../fields/field';
+import type { IVectorLayer } from '../../../layers/vector_layer';
+import { type RawValue, VECTOR_STYLES } from '../../../../../common/constants';
describe('percentilesValuesToFieldMeta', () => {
test('should return null when values is not defined', () => {
@@ -46,3 +49,59 @@ describe('percentilesValuesToFieldMeta', () => {
]);
});
});
+
+describe('getFieldMetaOptions', () => {
+ const dynamicColorOptions = {
+ color: 'Blues',
+ colorCategory: 'palette_0',
+ field: {
+ name: 'machine.os.keyword',
+ origin: 'source',
+ },
+ fieldMetaOptions: {
+ isEnabled: false,
+ },
+ type: 'CATEGORICAL',
+ };
+
+ test('should not automatically enable fieldMeta when field does support field meta from local', () => {
+ const property = new DynamicStyleProperty(
+ dynamicColorOptions,
+ VECTOR_STYLES.FILL_COLOR,
+ {
+ supportsFieldMetaFromLocalData: () => {
+ return true;
+ },
+ } as unknown as IField,
+ {} as unknown as IVectorLayer,
+ () => {
+ return (value: RawValue) => value + '_format';
+ }
+ );
+
+ const fieldMetaOptions = property.getFieldMetaOptions();
+ expect(fieldMetaOptions.isEnabled).toBe(false);
+ });
+
+ test('should automatically enable fieldMeta when field does not support field meta from local', () => {
+ const property = new DynamicStyleProperty(
+ dynamicColorOptions,
+ VECTOR_STYLES.FILL_COLOR,
+ {
+ supportsFieldMetaFromLocalData: () => {
+ return false;
+ },
+ } as unknown as IField,
+ {} as unknown as IVectorLayer,
+ () => {
+ return (value: RawValue) => value + '_format';
+ }
+ );
+
+ const fieldMetaOptions = property.getFieldMetaOptions();
+ expect(fieldMetaOptions.isEnabled).toBe(true);
+
+ // should not mutate original options state
+ expect(dynamicColorOptions.fieldMetaOptions.isEnabled).toBe(false);
+ });
+});
diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.tsx b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.tsx
index 40dd4eb8c524d..9fc1ccaf38c84 100644
--- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.tsx
+++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.tsx
@@ -341,11 +341,12 @@ export class DynamicStyleProperty
// The exact case that spawned this fix is with ES_SEARCH sources and 8.0 where vector tiles switched
// from vector tiles generated via Kibana server to vector tiles generated via Elasticsearch.
// Kibana vector tiles supported fieldMeta from local while Elasticsearch vector tiles do not support fieldMeta from local.
- if (this._field && !this._field.supportsFieldMetaFromLocalData()) {
- fieldMetaOptions.isEnabled = true;
- }
-
- return fieldMetaOptions;
+ return this._field && !this._field.supportsFieldMetaFromLocalData()
+ ? {
+ ...fieldMetaOptions,
+ isEnabled: true,
+ }
+ : fieldMetaOptions;
}
getDataMappingFunction() {
From 2d7311a5c95fbb550d0fc8fbfca5a73cb4c6ede7 Mon Sep 17 00:00:00 2001
From: Nicolas Chaulet
Date: Fri, 14 Jul 2023 11:17:12 -0400
Subject: [PATCH 26/50] [Fleet] Do not ignore coreMigrationVersion and
typeMigrationVersion when importing saved object (#161969)
---
.../services/epm/kibana/assets/install.ts | 22 ++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts
index 4dbceb772cc20..e0f307156dac1 100644
--- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts
+++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts
@@ -45,7 +45,12 @@ type SavedObjectToBe = Required & {
type: KibanaSavedObjectType;
};
@@ -81,13 +86,24 @@ export async function getKibanaAsset(key: string): Promise {
export function createSavedObjectKibanaAsset(asset: ArchiveAsset): SavedObjectToBe {
// convert that to an object
- return {
+ const so: Partial = {
type: asset.type,
id: asset.id,
attributes: asset.attributes,
references: asset.references || [],
- migrationVersion: asset.migrationVersion || {},
};
+
+ if (asset.migrationVersion) {
+ so.migrationVersion = asset.migrationVersion;
+ } else {
+ if (asset.coreMigrationVersion) {
+ so.coreMigrationVersion = asset.coreMigrationVersion;
+ }
+ if (asset.typeMigrationVersion) {
+ so.typeMigrationVersion = asset.typeMigrationVersion;
+ }
+ }
+ return so as SavedObjectToBe;
}
export async function installKibanaAssets(options: {
From 50c31c47d132d16d9f14bab046559603858fa44d Mon Sep 17 00:00:00 2001
From: Jon
Date: Fri, 14 Jul 2023 10:26:21 -0500
Subject: [PATCH 27/50] Update gitignore (#161975)
Includes a few compiled cypress typescript files generated during `node
scripts/type_check`. We're not using a global glob due to some plugins
using a javascript base.
---
.gitignore | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/.gitignore b/.gitignore
index 4999eb2fb5a17..86a789a1cfb5f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -99,6 +99,12 @@ x-pack/plugins/fleet/cypress.config.d.ts
x-pack/plugins/fleet/cypress.config.js
x-pack/plugins/osquery/cypress.config.d.ts
x-pack/plugins/osquery/cypress.config.js
+x-pack/plugins/enterprise_search/cypress.config.d.ts
+x-pack/plugins/enterprise_search/cypress.config.js
+x-pack/plugins/security_solution/public/management/cypress.config.d.ts
+x-pack/plugins/security_solution/public/management/cypress.config.js
+x-pack/plugins/security_solution/public/management/cypress_endpoint.config.d.ts
+x-pack/plugins/security_solution/public/management/cypress_endpoint.config.js
# release notes script output
report.csv
From 29b22a39416b61fa7d3d2109342665107e036c04 Mon Sep 17 00:00:00 2001
From: Chris Cowan
Date: Fri, 14 Jul 2023 09:53:00 -0600
Subject: [PATCH 28/50] [SLO] Unregistering from/to fields for histogram
indicator (#161907)
## Summary
This PR fixes #161861 by adding `shouldUnregister` to the `Controller`
components for `indicator.params.from` and `indicator.params.to` to
ensure the fields are unregistered when they are unmounted.
### Testing
1. Create a new SLO using the histogram indicator.
2. Set both the "good" and "total" events to use value count
3. Ensure the preview chart displays data
4. Set the "good" events to a range, the previous chart should update.
---
.../pages/slo_edit/components/histogram/histogram_indicator.tsx | 2 ++
1 file changed, 2 insertions(+)
diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator.tsx
index fd242dafde115..e966d0f3fb221 100644
--- a/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator.tsx
+++ b/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator.tsx
@@ -216,6 +216,7 @@ export function HistogramIndicator({ type, indexFields, isLoadingIndex }: Histog
defaultValue={NaN}
control={control}
rules={{ required: true }}
+ shouldUnregister
render={({ field: { ref, ...field }, fieldState }) => (
(
Date: Fri, 14 Jul 2023 18:04:10 +0200
Subject: [PATCH 29/50] [Defend workflows] Lift off technical preview badge
from Response Actions (#161826)
---
.../endpoint/action_type_field.tsx | 2 +-
.../response_actions_header.tsx | 44 +++++--------------
.../translations/translations/fr-FR.json | 2 -
.../translations/translations/ja-JP.json | 2 -
.../translations/translations/zh-CN.json | 2 -
5 files changed, 12 insertions(+), 40 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/action_type_field.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/action_type_field.tsx
index 384facb4901fc..cdc9fe8ef8d64 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/action_type_field.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/endpoint/action_type_field.tsx
@@ -90,7 +90,7 @@ const ActionTypeFieldComponent = ({
i18n.translate(
'xpack.securitySolution.responseActions.endpoint.validations.commandIsRequiredErrorMessage',
{
- defaultMessage: 'A command is required.',
+ defaultMessage: 'Action is a required field.',
}
)
),
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/response_actions_header.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/response_actions_header.tsx
index 8cab07ef08553..6ef673e004ceb 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/response_actions_header.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/response_actions_header.tsx
@@ -5,46 +5,24 @@
* 2.0.
*/
-import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui';
+import { EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
-import { i18n } from '@kbn/i18n';
import React from 'react';
export const ResponseActionsHeader = () => {
return (
<>
-
-
-
-