+
);
diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.tsx
index d2595354a4581..7afbbc8e5b89c 100644
--- a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.tsx
+++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.tsx
@@ -4,26 +4,39 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-import { EuiFlexGroup, EuiFlexItem, EuiFlyout } from '@elastic/eui';
-import React, { useState } from 'react';
+import { EuiFlexGroup, EuiFlexItem, EuiFlyout, EuiLink, EuiPanel, useEuiTheme } from '@elastic/eui';
+import { css } from '@emotion/css';
+import React from 'react';
+import { i18n } from '@kbn/i18n';
import type { Message } from '../../../common/types';
import { useCurrentUser } from '../../hooks/use_current_user';
import { useGenAIConnectors } from '../../hooks/use_genai_connectors';
import { useKibana } from '../../hooks/use_kibana';
import { useObservabilityAIAssistant } from '../../hooks/use_observability_ai_assistant';
+import { useObservabilityAIAssistantRouter } from '../../hooks/use_observability_ai_assistant_router';
import { getConnectorsManagementHref } from '../../utils/get_connectors_management_href';
import { ChatBody } from './chat_body';
+const containerClassName = css`
+ max-height: 100%;
+`;
+
export function ChatFlyout({
title,
messages,
+ conversationId,
isOpen,
onClose,
+ onChatUpdate,
+ onChatComplete,
}: {
title: string;
messages: Message[];
+ conversationId?: string;
isOpen: boolean;
onClose: () => void;
+ onChatUpdate?: (messages: Message[]) => void;
+ onChatComplete?: (messages: Message[]) => void;
}) {
const connectors = useGenAIConnectors();
@@ -33,13 +46,46 @@ export function ChatFlyout({
services: { http },
} = useKibana();
- const [isConversationListExpanded, setIsConversationListExpanded] = useState(false);
-
const service = useObservabilityAIAssistant();
+ const { euiTheme } = useEuiTheme();
+
+ const router = useObservabilityAIAssistantRouter();
+
return isOpen ? (
-
+
+
+
+ {conversationId ? (
+
+ {i18n.translate('xpack.observabilityAiAssistant.conversationDeepLinkLabel', {
+ defaultMessage: 'Open conversation',
+ })}
+
+ ) : (
+
+ {i18n.translate('xpack.observabilityAiAssistant.conversationListDeepLinkLabel', {
+ defaultMessage: 'Go to conversations',
+ })}
+
+ )}
+
+
- setIsConversationListExpanded(!isConversationListExpanded)
- }
- onChatComplete={() => {}}
- onChatUpdate={() => {}}
+ onChatUpdate={(nextMessages) => {
+ if (onChatUpdate) {
+ onChatUpdate(nextMessages);
+ }
+ }}
+ onChatComplete={(nextMessages) => {
+ if (onChatComplete) {
+ onChatComplete(nextMessages);
+ }
+ }}
/>
diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_kibana.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_kibana.ts
index 41239c6e4af1a..d9ab341dce80d 100644
--- a/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_kibana.ts
+++ b/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_kibana.ts
@@ -15,6 +15,11 @@ export function useKibana() {
}
},
},
+ http: {
+ basePath: {
+ prepend: () => '',
+ },
+ },
notifications: {
toasts: {
addSuccess: () => {},
diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_observability_ai_assistant.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_observability_ai_assistant.ts
new file mode 100644
index 0000000000000..15aa5d0428ab3
--- /dev/null
+++ b/x-pack/plugins/observability_ai_assistant/public/hooks/__storybook_mocks__/use_observability_ai_assistant.ts
@@ -0,0 +1,10 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+export function useObservabilityAIAssistant() {
+ return {};
+}
diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversation.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversation.ts
new file mode 100644
index 0000000000000..39e64c5c95666
--- /dev/null
+++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversation.ts
@@ -0,0 +1,112 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import { i18n } from '@kbn/i18n';
+import { merge, omit } from 'lodash';
+import { Dispatch, SetStateAction, useState } from 'react';
+import type { Conversation, Message } from '../../common';
+import type { ConversationCreateRequest } from '../../common/types';
+import { useAbortableAsync, type AbortableAsyncState } from './use_abortable_async';
+import { useKibana } from './use_kibana';
+import { useObservabilityAIAssistant } from './use_observability_ai_assistant';
+import { createNewConversation } from './use_timeline';
+
+export function useConversation(conversationId?: string): {
+ conversation: AbortableAsyncState;
+ displayedMessages: Message[];
+ setDisplayedMessages: Dispatch>;
+ save: (messages: Message[]) => Promise;
+} {
+ const service = useObservabilityAIAssistant();
+
+ const {
+ services: { notifications },
+ } = useKibana();
+
+ const [displayedMessages, setDisplayedMessages] = useState([]);
+
+ const conversation: AbortableAsyncState =
+ useAbortableAsync(
+ ({ signal }) => {
+ if (!conversationId) {
+ const nextConversation = createNewConversation();
+ setDisplayedMessages(nextConversation.messages);
+ return nextConversation;
+ }
+
+ return service
+ .callApi('GET /internal/observability_ai_assistant/conversation/{conversationId}', {
+ signal,
+ params: { path: { conversationId } },
+ })
+ .then((nextConversation) => {
+ setDisplayedMessages(nextConversation.messages);
+ return nextConversation;
+ })
+ .catch((error) => {
+ setDisplayedMessages([]);
+ throw error;
+ });
+ },
+ [conversationId]
+ );
+
+ return {
+ conversation,
+ displayedMessages,
+ setDisplayedMessages,
+ save: (messages: Message[]) => {
+ const conversationObject = conversation.value!;
+ return conversationId
+ ? service
+ .callApi(`POST /internal/observability_ai_assistant/conversation/{conversationId}`, {
+ signal: null,
+ params: {
+ path: {
+ conversationId,
+ },
+ body: {
+ conversation: merge(
+ {
+ '@timestamp': conversationObject['@timestamp'],
+ conversation: {
+ id: conversationId,
+ },
+ },
+ omit(conversationObject, 'conversation.last_updated', 'namespace', 'user'),
+ { messages }
+ ),
+ },
+ },
+ })
+ .catch((err) => {
+ notifications.toasts.addError(err, {
+ title: i18n.translate('xpack.observabilityAiAssistant.errorUpdatingConversation', {
+ defaultMessage: 'Could not update conversation',
+ }),
+ });
+ throw err;
+ })
+ : service
+ .callApi(`PUT /internal/observability_ai_assistant/conversation`, {
+ signal: null,
+ params: {
+ body: {
+ conversation: merge({}, conversationObject, { messages }),
+ },
+ },
+ })
+ .catch((err) => {
+ notifications.toasts.addError(err, {
+ title: i18n.translate('xpack.observabilityAiAssistant.errorCreatingConversation', {
+ defaultMessage: 'Could not create conversation',
+ }),
+ });
+ throw err;
+ });
+ },
+ };
+}
diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_observability_ai_assistant.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_observability_ai_assistant.ts
index 8d8938fc49521..041d2ebcba498 100644
--- a/x-pack/plugins/observability_ai_assistant/public/hooks/use_observability_ai_assistant.ts
+++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_observability_ai_assistant.ts
@@ -7,6 +7,12 @@
import { useContext } from 'react';
import { ObservabilityAIAssistantContext } from '../context/observability_ai_assistant_provider';
+export function useObservabilityAIAssistantOptional() {
+ const services = useContext(ObservabilityAIAssistantContext);
+
+ return services;
+}
+
export function useObservabilityAIAssistant() {
const services = useContext(ObservabilityAIAssistantContext);
diff --git a/x-pack/plugins/observability_ai_assistant/public/index.ts b/x-pack/plugins/observability_ai_assistant/public/index.ts
index bdb716557db5c..18039652eaa66 100644
--- a/x-pack/plugins/observability_ai_assistant/public/index.ts
+++ b/x-pack/plugins/observability_ai_assistant/public/index.ts
@@ -20,11 +20,22 @@ export const ContextualInsight = withSuspense(
lazy(() => import('./components/insight/insight').then((m) => ({ default: m.Insight })))
);
+export const ObservabilityAIAssistantActionMenuItem = withSuspense(
+ lazy(() =>
+ import('./components/action_menu_item/action_menu_item').then((m) => ({
+ default: m.ObservabilityAIAssistantActionMenuItem,
+ }))
+ )
+);
+
export { ObservabilityAIAssistantProvider } from './context/observability_ai_assistant_provider';
export type { ObservabilityAIAssistantPluginSetup, ObservabilityAIAssistantPluginStart };
-export { useObservabilityAIAssistant } from './hooks/use_observability_ai_assistant';
+export {
+ useObservabilityAIAssistant,
+ useObservabilityAIAssistantOptional,
+} from './hooks/use_observability_ai_assistant';
export type { Conversation, Message } from '../common';
export { MessageRole } from '../common';
diff --git a/x-pack/plugins/observability_ai_assistant/public/plugin.tsx b/x-pack/plugins/observability_ai_assistant/public/plugin.tsx
index 6ceff112ada4c..4c0b184131557 100644
--- a/x-pack/plugins/observability_ai_assistant/public/plugin.tsx
+++ b/x-pack/plugins/observability_ai_assistant/public/plugin.tsx
@@ -68,7 +68,7 @@ export class ObservabilityAIAssistantPlugin
title: i18n.translate('xpack.observabilityAiAssistant.conversationsDeepLinkTitle', {
defaultMessage: 'Conversations',
}),
- path: '/conversations',
+ path: '/conversations/new',
},
],
diff --git a/x-pack/plugins/observability_ai_assistant/public/routes/config.tsx b/x-pack/plugins/observability_ai_assistant/public/routes/config.tsx
index db3384d7005d5..f4245cb69d9e7 100644
--- a/x-pack/plugins/observability_ai_assistant/public/routes/config.tsx
+++ b/x-pack/plugins/observability_ai_assistant/public/routes/config.tsx
@@ -38,6 +38,9 @@ const observabilityAIAssistantRoutes = {
}),
element: ,
},
+ '/conversations': {
+ element: ,
+ },
},
},
};
diff --git a/x-pack/plugins/observability_ai_assistant/public/routes/conversations/conversation_view.tsx b/x-pack/plugins/observability_ai_assistant/public/routes/conversations/conversation_view.tsx
index 4ffaa69d2acf8..2092390aedb90 100644
--- a/x-pack/plugins/observability_ai_assistant/public/routes/conversations/conversation_view.tsx
+++ b/x-pack/plugins/observability_ai_assistant/public/routes/conversations/conversation_view.tsx
@@ -7,20 +7,18 @@
import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiSpacer } from '@elastic/eui';
import { css } from '@emotion/css';
import { i18n } from '@kbn/i18n';
-import { merge, omit } from 'lodash';
import React, { useMemo, useState } from 'react';
-import type { ConversationCreateRequest, Message } from '../../../common/types';
import { ChatBody } from '../../components/chat/chat_body';
import { ConversationList } from '../../components/chat/conversation_list';
-import { AbortableAsyncState, useAbortableAsync } from '../../hooks/use_abortable_async';
+import { useAbortableAsync } from '../../hooks/use_abortable_async';
import { useConfirmModal } from '../../hooks/use_confirm_modal';
+import { useConversation } from '../../hooks/use_conversation';
import { useCurrentUser } from '../../hooks/use_current_user';
import { useGenAIConnectors } from '../../hooks/use_genai_connectors';
import { useKibana } from '../../hooks/use_kibana';
import { useObservabilityAIAssistant } from '../../hooks/use_observability_ai_assistant';
import { useObservabilityAIAssistantParams } from '../../hooks/use_observability_ai_assistant_params';
import { useObservabilityAIAssistantRouter } from '../../hooks/use_observability_ai_assistant_router';
-import { createNewConversation } from '../../hooks/use_timeline';
import { EMPTY_CONVERSATION_TITLE } from '../../i18n';
import { getConnectorsManagementHref } from '../../utils/get_connectors_management_href';
@@ -63,31 +61,8 @@ export function ConversationView() {
const conversationId = 'conversationId' in path ? path.conversationId : undefined;
- const conversation: AbortableAsyncState =
- useAbortableAsync(
- ({ signal }) => {
- if (!conversationId) {
- const nextConversation = createNewConversation();
- setDisplayedMessages(nextConversation.messages);
- return nextConversation;
- }
-
- return service
- .callApi('GET /internal/observability_ai_assistant/conversation/{conversationId}', {
- signal,
- params: { path: { conversationId } },
- })
- .then((nextConversation) => {
- setDisplayedMessages(nextConversation.messages);
- return nextConversation;
- })
- .catch((error) => {
- setDisplayedMessages([]);
- throw error;
- });
- },
- [conversationId]
- );
+ const { conversation, displayedMessages, setDisplayedMessages, save } =
+ useConversation(conversationId);
const conversations = useAbortableAsync(
({ signal }) => {
@@ -113,8 +88,6 @@ export function ConversationView() {
];
}, [conversations.value?.conversations, conversationId, observabilityAIAssistantRouter]);
- const [displayedMessages, setDisplayedMessages] = useState([]);
-
function navigateToConversation(nextConversationId?: string) {
observabilityAIAssistantRouter.push(
nextConversationId ? '/conversations/{conversationId}' : '/conversations/new',
@@ -233,71 +206,14 @@ export function ConversationView() {
service={service}
messages={displayedMessages}
onChatComplete={(messages) => {
- const conversationObject = conversation.value!;
- if (conversationId) {
- service
- .callApi(
- `POST /internal/observability_ai_assistant/conversation/{conversationId}`,
- {
- signal: null,
- params: {
- path: {
- conversationId,
- },
- body: {
- conversation: merge(
- {
- '@timestamp': conversationObject['@timestamp'],
- conversation: {
- id: conversationId,
- },
- },
- omit(
- conversationObject,
- 'conversation.last_updated',
- 'namespace',
- 'user'
- ),
- { messages }
- ),
- },
- },
- }
- )
- .then(() => {
- conversations.refresh();
- })
- .catch((err) => {
- notifications.toasts.addError(err, {
- title: i18n.translate(
- 'xpack.observabilityAiAssistant.errorCreatingConversation',
- { defaultMessage: 'Could not create conversation' }
- ),
- });
- });
- } else {
- service
- .callApi(`PUT /internal/observability_ai_assistant/conversation`, {
- signal: null,
- params: {
- body: {
- conversation: merge({}, conversationObject, { messages }),
- },
- },
- })
- .then((createdConversation) => {
- navigateToConversation(createdConversation.conversation.id);
- conversations.refresh();
- })
- .catch((err) => {
- notifications.toasts.addError(err, {
- title: i18n.translate(
- 'xpack.observabilityAiAssistant.errorCreatingConversation',
- { defaultMessage: 'Could not create conversation' }
- ),
- });
- });
- }
+ save(messages)
+ .then((nextConversation) => {
+ conversations.refresh();
+ if (!conversationId) {
+ navigateToConversation(nextConversation.conversation.id);
+ }
+ })
+ .catch(() => {});
}}
onChatUpdate={(messages) => {
setDisplayedMessages(messages);
diff --git a/x-pack/plugins/profiling/public/components/profiling_header_action_menu.tsx b/x-pack/plugins/profiling/public/components/profiling_header_action_menu.tsx
index b973c763142be..fafcc1b36d9d8 100644
--- a/x-pack/plugins/profiling/public/components/profiling_header_action_menu.tsx
+++ b/x-pack/plugins/profiling/public/components/profiling_header_action_menu.tsx
@@ -7,6 +7,7 @@
import { EuiFlexGroup, EuiFlexItem, EuiHeaderLink, EuiHeaderLinks, EuiIcon } from '@elastic/eui';
import React from 'react';
import { i18n } from '@kbn/i18n';
+import { ObservabilityAIAssistantActionMenuItem } from '@kbn/observability-ai-assistant-plugin/public';
import { useProfilingRouter } from '../hooks/use_profiling_router';
import { NoDataTabs } from '../views/no_data_view';
@@ -31,6 +32,7 @@ export function ProfilingHeaderActionMenu() {
+
);
}
diff --git a/x-pack/plugins/synthetics/kibana.jsonc b/x-pack/plugins/synthetics/kibana.jsonc
index bfef1566d7e9c..511666996f829 100644
--- a/x-pack/plugins/synthetics/kibana.jsonc
+++ b/x-pack/plugins/synthetics/kibana.jsonc
@@ -24,6 +24,7 @@
"licensing",
"observability",
"observabilityShared",
+ "observabilityAIAssistant",
"ruleRegistry",
"security",
"share",
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/header/action_menu_content.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/header/action_menu_content.tsx
index d74492794d08f..a123f4be95382 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/header/action_menu_content.tsx
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/header/action_menu_content.tsx
@@ -11,6 +11,7 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { useHistory, useRouteMatch } from 'react-router-dom';
import { createExploratoryViewUrl } from '@kbn/exploratory-view-plugin/public';
+import { ObservabilityAIAssistantActionMenuItem } from '@kbn/observability-ai-assistant-plugin/public';
import { LastRefreshed } from '../components/last_refreshed';
import { AutoRefreshButton } from '../components/auto_refresh_button';
import { useSyntheticsSettingsContext } from '../../../contexts';
@@ -102,8 +103,8 @@ export function ActionMenuContent(): React.ReactElement {
{ANALYZE_DATA}
-
+
);
}
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/synthetics_app.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/synthetics_app.tsx
index 1be49596f1480..f9b37b64df403 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/synthetics_app.tsx
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/synthetics_app.tsx
@@ -17,6 +17,7 @@ import {
} from '@kbn/kibana-react-plugin/public';
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
import { InspectorContextProvider } from '@kbn/observability-shared-plugin/public';
+import { ObservabilityAIAssistantProvider } from '@kbn/observability-ai-assistant-plugin/public';
import { SyntheticsAppProps } from './contexts';
import {
@@ -98,30 +99,32 @@ const Application = (props: SyntheticsAppProps) => {
fleet: startPlugins.fleet,
}}
>
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/x-pack/plugins/synthetics/public/plugin.ts b/x-pack/plugins/synthetics/public/plugin.ts
index 28802415c38bf..28ac4ba859e5f 100644
--- a/x-pack/plugins/synthetics/public/plugin.ts
+++ b/x-pack/plugins/synthetics/public/plugin.ts
@@ -49,6 +49,10 @@ import type {
ObservabilitySharedPluginSetup,
ObservabilitySharedPluginStart,
} from '@kbn/observability-shared-plugin/public';
+import {
+ ObservabilityAIAssistantPluginStart,
+ ObservabilityAIAssistantPluginSetup,
+} from '@kbn/observability-ai-assistant-plugin/public';
import { PLUGIN } from '../common/constants/plugin';
import { OVERVIEW_ROUTE } from '../common/constants/ui';
import { locators } from './apps/locators';
@@ -61,6 +65,7 @@ export interface ClientPluginsSetup {
exploratoryView: ExploratoryViewPublicSetup;
observability: ObservabilityPublicSetup;
observabilityShared: ObservabilitySharedPluginSetup;
+ observabilityAIAssistant: ObservabilityAIAssistantPluginSetup;
share: SharePluginSetup;
triggersActionsUi: TriggersAndActionsUIPublicPluginSetup;
cloud?: CloudSetup;
@@ -76,6 +81,7 @@ export interface ClientPluginsStart {
exploratoryView: ExploratoryViewPublicStart;
observability: ObservabilityPublicStart;
observabilityShared: ObservabilitySharedPluginStart;
+ observabilityAIAssistant: ObservabilityAIAssistantPluginStart;
share: SharePluginStart;
triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
cases: CasesUiStart;
diff --git a/x-pack/plugins/synthetics/tsconfig.json b/x-pack/plugins/synthetics/tsconfig.json
index 85727394263a6..12bc371fef915 100644
--- a/x-pack/plugins/synthetics/tsconfig.json
+++ b/x-pack/plugins/synthetics/tsconfig.json
@@ -76,6 +76,7 @@
"@kbn/std",
"@kbn/core-saved-objects-server-mocks",
"@kbn/shared-ux-page-kibana-template",
+ "@kbn/observability-ai-assistant-plugin",
],
"exclude": [
"target/**/*",
diff --git a/x-pack/plugins/uptime/kibana.jsonc b/x-pack/plugins/uptime/kibana.jsonc
index 9d082f5f1a70b..df0b2e13839cf 100644
--- a/x-pack/plugins/uptime/kibana.jsonc
+++ b/x-pack/plugins/uptime/kibana.jsonc
@@ -24,6 +24,7 @@
"licensing",
"observability",
"observabilityShared",
+ "observabilityAIAssistant",
"ruleRegistry",
"security",
"share",
diff --git a/x-pack/plugins/uptime/public/legacy_uptime/app/uptime_app.tsx b/x-pack/plugins/uptime/public/legacy_uptime/app/uptime_app.tsx
index c967c732ec8d4..fb45099888592 100644
--- a/x-pack/plugins/uptime/public/legacy_uptime/app/uptime_app.tsx
+++ b/x-pack/plugins/uptime/public/legacy_uptime/app/uptime_app.tsx
@@ -18,6 +18,7 @@ import {
} from '@kbn/kibana-react-plugin/public';
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
import { InspectorContextProvider } from '@kbn/observability-shared-plugin/public';
+import { ObservabilityAIAssistantProvider } from '@kbn/observability-ai-assistant-plugin/public';
import { ClientPluginsSetup, ClientPluginsStart } from '../../plugin';
import { UMUpdateBadge } from '../lib/lib';
import {
@@ -129,32 +130,34 @@ const Application = (props: UptimeAppProps) => {
cases: startPlugins.cases,
}}
>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/x-pack/plugins/uptime/public/legacy_uptime/components/common/header/action_menu_content.tsx b/x-pack/plugins/uptime/public/legacy_uptime/components/common/header/action_menu_content.tsx
index 38047aff244b4..d6a37ea9bd4ee 100644
--- a/x-pack/plugins/uptime/public/legacy_uptime/components/common/header/action_menu_content.tsx
+++ b/x-pack/plugins/uptime/public/legacy_uptime/components/common/header/action_menu_content.tsx
@@ -13,6 +13,7 @@ import { useHistory, useRouteMatch } from 'react-router-dom';
import { useSelector } from 'react-redux';
import { createExploratoryViewUrl } from '@kbn/exploratory-view-plugin/public';
import { useKibana } from '@kbn/kibana-react-plugin/public';
+import { ObservabilityAIAssistantActionMenuItem } from '@kbn/observability-ai-assistant-plugin/public';
import { stringifyUrlParams } from '../../../lib/helper/url_params/stringify_url_params';
import { useUptimeSettingsContext } from '../../../contexts/uptime_settings_context';
import { useGetUrlParams } from '../../../hooks';
@@ -116,6 +117,7 @@ export function ActionMenuContent(): React.ReactElement {
{ADD_DATA_LABEL}
+
);
}
diff --git a/x-pack/plugins/uptime/public/plugin.ts b/x-pack/plugins/uptime/public/plugin.ts
index 0b4942e4020d7..7a13252327a90 100644
--- a/x-pack/plugins/uptime/public/plugin.ts
+++ b/x-pack/plugins/uptime/public/plugin.ts
@@ -51,6 +51,10 @@ import type {
ObservabilitySharedPluginStart,
} from '@kbn/observability-shared-plugin/public';
import { AppStatus, AppUpdater } from '@kbn/core-application-browser';
+import {
+ ObservabilityAIAssistantPluginStart,
+ ObservabilityAIAssistantPluginSetup,
+} from '@kbn/observability-ai-assistant-plugin/public';
import { PLUGIN } from '../common/constants/plugin';
import {
LazySyntheticsPolicyCreateExtension,
@@ -69,6 +73,7 @@ export interface ClientPluginsSetup {
exploratoryView: ExploratoryViewPublicSetup;
observability: ObservabilityPublicSetup;
observabilityShared: ObservabilitySharedPluginSetup;
+ observabilityAIAssistant: ObservabilityAIAssistantPluginSetup;
share: SharePluginSetup;
triggersActionsUi: TriggersAndActionsUIPublicPluginSetup;
cloud?: CloudSetup;
@@ -84,6 +89,7 @@ export interface ClientPluginsStart {
exploratoryView: ExploratoryViewPublicStart;
observability: ObservabilityPublicStart;
observabilityShared: ObservabilitySharedPluginStart;
+ observabilityAIAssistant: ObservabilityAIAssistantPluginStart;
share: SharePluginStart;
triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
cases: CasesUiStart;
diff --git a/x-pack/plugins/uptime/tsconfig.json b/x-pack/plugins/uptime/tsconfig.json
index 0bc244a45de0e..ddfe50db65940 100644
--- a/x-pack/plugins/uptime/tsconfig.json
+++ b/x-pack/plugins/uptime/tsconfig.json
@@ -72,6 +72,7 @@
"@kbn/core-http-router-server-internal",
"@kbn/actions-plugin",
"@kbn/core-saved-objects-server",
+ "@kbn/observability-ai-assistant-plugin",
],
"exclude": [
"target/**/*",
diff --git a/x-pack/plugins/ux/kibana.jsonc b/x-pack/plugins/ux/kibana.jsonc
index 341d9d8fcc1f8..26a2ab78a926a 100644
--- a/x-pack/plugins/ux/kibana.jsonc
+++ b/x-pack/plugins/ux/kibana.jsonc
@@ -15,6 +15,7 @@
"licensing",
"triggersActionsUi",
"observabilityShared",
+ "observabilityAIAssistant",
"embeddable",
"infra",
"inspector",
diff --git a/x-pack/plugins/ux/public/application/ux_app.tsx b/x-pack/plugins/ux/public/application/ux_app.tsx
index 85af82597863a..aa1ae124d0279 100644
--- a/x-pack/plugins/ux/public/application/ux_app.tsx
+++ b/x-pack/plugins/ux/public/application/ux_app.tsx
@@ -32,6 +32,7 @@ import {
InspectorContextProvider,
useBreadcrumbs,
} from '@kbn/observability-shared-plugin/public';
+import { ObservabilityAIAssistantProvider } from '@kbn/observability-ai-assistant-plugin/public';
import { CsmSharedContextProvider } from '../components/app/rum_dashboard/csm_shared_context';
import {
DASHBOARD_LABEL,
@@ -111,6 +112,7 @@ export function UXAppRoot({
maps,
observability,
observabilityShared,
+ observabilityAIAssistant,
exploratoryView,
data,
dataViews,
@@ -147,40 +149,42 @@ export function UXAppRoot({
lens,
}}
>
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
diff --git a/x-pack/plugins/ux/public/components/app/rum_dashboard/action_menu/index.tsx b/x-pack/plugins/ux/public/components/app/rum_dashboard/action_menu/index.tsx
index 4a18759a0aebe..88709a068fea8 100644
--- a/x-pack/plugins/ux/public/components/app/rum_dashboard/action_menu/index.tsx
+++ b/x-pack/plugins/ux/public/components/app/rum_dashboard/action_menu/index.tsx
@@ -14,6 +14,7 @@ import {
createExploratoryViewUrl,
} from '@kbn/exploratory-view-plugin/public';
import { AppMountParameters } from '@kbn/core/public';
+import { ObservabilityAIAssistantActionMenuItem } from '@kbn/observability-ai-assistant-plugin/public';
import { useLegacyUrlParams } from '../../../../context/url_params_context/use_url_params';
import { SERVICE_NAME } from '../../../../../common/elasticsearch_fieldnames';
import { UxInspectorHeaderLink } from './inpector_link';
@@ -85,6 +86,7 @@ export function UXActionMenu({
})}
+
);
diff --git a/x-pack/plugins/ux/public/plugin.ts b/x-pack/plugins/ux/public/plugin.ts
index a578b6aca0b79..9cb846238aa47 100644
--- a/x-pack/plugins/ux/public/plugin.ts
+++ b/x-pack/plugins/ux/public/plugin.ts
@@ -42,6 +42,10 @@ import {
ObservabilitySharedPluginSetup,
ObservabilitySharedPluginStart,
} from '@kbn/observability-shared-plugin/public';
+import {
+ ObservabilityAIAssistantPluginStart,
+ ObservabilityAIAssistantPluginSetup,
+} from '@kbn/observability-ai-assistant-plugin/public';
export type UxPluginSetup = void;
export type UxPluginStart = void;
@@ -54,6 +58,7 @@ export interface ApmPluginSetupDeps {
licensing: LicensingPluginSetup;
observability: ObservabilityPublicSetup;
observabilityShared: ObservabilitySharedPluginSetup;
+ observabilityAIAssistant: ObservabilityAIAssistantPluginSetup;
}
export interface ApmPluginStartDeps {
@@ -65,6 +70,7 @@ export interface ApmPluginStartDeps {
inspector: InspectorPluginStart;
observability: ObservabilityPublicStart;
observabilityShared: ObservabilitySharedPluginStart;
+ observabilityAIAssistant: ObservabilityAIAssistantPluginStart;
exploratoryView: ExploratoryViewPublicStart;
dataViews: DataViewsPublicPluginStart;
lens: LensPublicStart;
diff --git a/x-pack/plugins/ux/tsconfig.json b/x-pack/plugins/ux/tsconfig.json
index e91b3b24198f6..0b2adfd3e66b7 100644
--- a/x-pack/plugins/ux/tsconfig.json
+++ b/x-pack/plugins/ux/tsconfig.json
@@ -39,6 +39,7 @@
"@kbn/exploratory-view-plugin",
"@kbn/observability-shared-plugin",
"@kbn/shared-ux-router",
+ "@kbn/observability-ai-assistant-plugin",
],
"exclude": [
"target/**/*",
From 290bf1d978c89c5d1f8e88e687c3be26f1f6cc5d Mon Sep 17 00:00:00 2001
From: Giorgos Bamparopoulos
Date: Fri, 11 Aug 2023 09:27:21 +0100
Subject: [PATCH 38/45] [APM] Make service group saved objects exportable
(#163569)
- Makes the saved objects used by service groups exportable / importable
- Adds a link from the saved object to the service group page
- Renames the saved objects from `APM Service Groups` to `Service Group:
---
.../apm/server/saved_objects/apm_service_groups.ts | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/x-pack/plugins/apm/server/saved_objects/apm_service_groups.ts b/x-pack/plugins/apm/server/saved_objects/apm_service_groups.ts
index 687c48260a085..180417d3dc42e 100644
--- a/x-pack/plugins/apm/server/saved_objects/apm_service_groups.ts
+++ b/x-pack/plugins/apm/server/saved_objects/apm_service_groups.ts
@@ -52,12 +52,16 @@ export const apmServiceGroups: SavedObjectsType = {
},
},
management: {
- importableAndExportable: false,
+ importableAndExportable: true,
icon: 'apmApp',
- getTitle: () =>
- i18n.translate('xpack.apm.apmServiceGroups.title', {
- defaultMessage: 'APM Service Groups',
- }),
+ getTitle: (savedObject) =>
+ `${i18n.translate('xpack.apm.apmServiceGroups.title', {
+ defaultMessage: 'Service Group',
+ })}: ${savedObject.attributes.groupName}`,
+ getInAppUrl: (savedObject) => ({
+ path: `/app/apm/services?serviceGroup=${savedObject.id}`,
+ uiCapabilitiesPath: 'apm.show',
+ }),
},
modelVersions: {
'1': {
From 5de69cb567e8f6fde93f09b839710e1ee8a3cc72 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?=
Date: Fri, 11 Aug 2023 10:32:32 +0200
Subject: [PATCH 39/45] [Flaky #118272] Unskip tests (#163319)
---
.../saved_objects_management_security.ts | 43 ++++++++++++-------
1 file changed, 27 insertions(+), 16 deletions(-)
diff --git a/x-pack/test/functional/apps/saved_objects_management/feature_controls/saved_objects_management_security.ts b/x-pack/test/functional/apps/saved_objects_management/feature_controls/saved_objects_management_security.ts
index c0b81ed8443e5..a626c613fd50d 100644
--- a/x-pack/test/functional/apps/saved_objects_management/feature_controls/saved_objects_management_security.ts
+++ b/x-pack/test/functional/apps/saved_objects_management/feature_controls/saved_objects_management_security.ts
@@ -16,10 +16,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
let version: string = '';
const find = getService('find');
- // FLAKY: https://github.com/elastic/kibana/issues/118272
- describe.skip('feature controls saved objects management', () => {
+ describe('feature controls saved objects management', () => {
before(async () => {
- version = await kibanaServer.version.get();
+ // version = await kibanaServer.version.get();
+ // Using the version below instead because we don't need the extra `-SNAPSHOT` bit
+ version = (await kibanaServer.status.get()).version.number;
await kibanaServer.importExport.load(
'x-pack/test/functional/fixtures/kbn_archiver/saved_objects_management/feature_controls/security'
);
@@ -76,10 +77,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
it('shows all saved objects', async () => {
const objects = await PageObjects.savedObjects.getRowTitles();
expect(objects).to.eql([
- `Advanced Settings [${version}]`,
- 'A Dashboard',
'logstash-*',
'A Pie',
+ 'A Dashboard',
+ `Global Settings [${version}]`,
+ `Advanced Settings [${version}]`,
]);
});
@@ -87,20 +89,24 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
const bools = await PageObjects.savedObjects.getTableSummary();
expect(bools).to.eql([
{
- title: `Advanced Settings [${version}]`,
- canViewInApp: false,
+ title: 'logstash-*',
+ canViewInApp: true,
},
{
- title: 'A Dashboard',
+ title: 'A Pie',
canViewInApp: true,
},
{
- title: 'logstash-*',
+ title: 'A Dashboard',
canViewInApp: true,
},
{
- title: 'A Pie',
- canViewInApp: true,
+ title: `Global Settings [${version}]`,
+ canViewInApp: false,
+ },
+ {
+ title: `Advanced Settings [${version}]`,
+ canViewInApp: false,
},
]);
});
@@ -195,10 +201,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
it('shows all saved objects', async () => {
const objects = await PageObjects.savedObjects.getRowTitles();
expect(objects).to.eql([
- `Advanced Settings [${version}]`,
- 'A Dashboard',
'logstash-*',
'A Pie',
+ 'A Dashboard',
+ `Global Settings [${version}]`,
+ `Advanced Settings [${version}]`,
]);
});
@@ -206,7 +213,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
const bools = await PageObjects.savedObjects.getTableSummary();
expect(bools).to.eql([
{
- title: `Advanced Settings [${version}]`,
+ title: 'logstash-*',
+ canViewInApp: false,
+ },
+ {
+ title: 'A Pie',
canViewInApp: false,
},
{
@@ -214,11 +225,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
canViewInApp: false,
},
{
- title: 'logstash-*',
+ title: `Global Settings [${version}]`,
canViewInApp: false,
},
{
- title: 'A Pie',
+ title: `Advanced Settings [${version}]`,
canViewInApp: false,
},
]);
From e944a19cbd5506cf5431e0d8cd113d53760aece0 Mon Sep 17 00:00:00 2001
From: Anton Dosov
Date: Fri, 11 Aug 2023 10:33:50 +0200
Subject: [PATCH 40/45] [Serverless] Partially fix lens/maps/visualize
breadcrumbs missing title (#163476)
## Summary
Partially address https://github.com/elastic/kibana/issues/163337 for
lens, visualize, maps
### Context:
In serverless navigation, we changed how breadcrumbs work. Instead of
setting the full path manually, we automatically calculate the main
parts of the path from the side nav + current URL. This was done to keep
side nav and breadcrumbs in sync as much as possible and solve
consistency issues with breadcrumbs across apps.
https://docs.elastic.dev/kibana-dev-docs/serverless-project-navigation#breadcrumbs
Apps can append custom deeper context using the
`serverless.setBreadcrumbs` API. Regular `core.chrome.setBreadcrumbs`
has no effect when the serverless nav is rendered.
### Fix
This PR fixes lens, visualize, and maps to add "title" breadcrumb in
serverless. **Unfortunately, it doesn't fully restore the full
breadcrumbs functionality visualize/maps/lens have in the non-serverless
Kibana:**
In the non-serverless Kibana lens/visualize/maps have sophisticated
breadcrumbs where context takes into account `ByValue` and
`originatingApp` and can switch depending on the context. For example,
if the user is coming from "Dashboard" to edit "byValue" Lens
visualization, Lens breadcrumbs display "Dashboard > Create", instead of
"Visualization > Create".
Currently, we can't repeat this behavior with serverless breadcrumbs
because the context is set by the navigation config, e.g.:
https://github.com/elastic/kibana/blob/9538fab0909d71eee8e371afd0d84b0eaf2ccfa7/x-pack/plugins/serverless_observability/public/components/side_navigation/index.tsx#L136-L141
In this PR I attempt to do a quick fix for the serverless breadcrumbs by
simply appending the last ("title") part of the breadcrumb. In a follow
up we need to think about how to bring back the original breadcrumbs
functionality with changing `Visualize <-> Dashboard` context. We also
will need to figure out how to sync the changing context with the side
nav, as we don't want to show "Dashboard" in the breadcrumb, but have
"Visualization" highlighted in the side nav. Here is the issue:
https://github.com/elastic/kibana/issues/163488
---
src/plugins/visualizations/kibana.jsonc | 3 +-
src/plugins/visualizations/public/plugin.ts | 3 ++
.../components/visualize_listing.tsx | 22 ++++++---
.../public/visualize_app/types.ts | 2 +
.../public/visualize_app/utils/breadcrumbs.ts | 45 +++++++++++++++++++
.../utils/get_top_nav_config.tsx | 9 +++-
.../utils/use/use_saved_vis_instance.ts | 45 ++++++++++++++-----
.../utils/use/use_vis_byvalue.ts | 16 +++++--
src/plugins/visualizations/tsconfig.json | 3 +-
x-pack/plugins/lens/kibana.jsonc | 3 +-
.../lens/public/app_plugin/app.test.tsx | 25 +++++++++++
x-pack/plugins/lens/public/app_plugin/app.tsx | 16 ++++++-
.../lens/public/app_plugin/mounter.tsx | 2 +
.../plugins/lens/public/app_plugin/types.ts | 2 +
x-pack/plugins/lens/public/plugin.ts | 2 +
x-pack/plugins/lens/tsconfig.json | 1 +
x-pack/plugins/maps/kibana.jsonc | 3 +-
x-pack/plugins/maps/public/kibana_services.ts | 1 +
x-pack/plugins/maps/public/plugin.ts | 2 +
.../routes/list_page/maps_list_view.tsx | 7 ++-
.../routes/map_page/saved_map/saved_map.ts | 27 +++++++----
x-pack/plugins/maps/tsconfig.json | 1 +
22 files changed, 200 insertions(+), 40 deletions(-)
diff --git a/src/plugins/visualizations/kibana.jsonc b/src/plugins/visualizations/kibana.jsonc
index f681595cedd7a..22c6bd9dd32b2 100644
--- a/src/plugins/visualizations/kibana.jsonc
+++ b/src/plugins/visualizations/kibana.jsonc
@@ -33,7 +33,8 @@
"home",
"share",
"spaces",
- "savedObjectsTaggingOss"
+ "savedObjectsTaggingOss",
+ "serverless"
],
"requiredBundles": [
"kibanaUtils",
diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts
index 2b906620e5f8b..3ca1672159f24 100644
--- a/src/plugins/visualizations/public/plugin.ts
+++ b/src/plugins/visualizations/public/plugin.ts
@@ -59,6 +59,7 @@ import type { SpacesPluginStart } from '@kbn/spaces-plugin/public';
import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public';
import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public';
import type { SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public';
+import type { ServerlessPluginStart } from '@kbn/serverless/public';
import {
ContentManagementPublicSetup,
ContentManagementPublicStart,
@@ -164,6 +165,7 @@ export interface VisualizationsStartDeps {
usageCollection: UsageCollectionStart;
savedObjectsManagement: SavedObjectsManagementPluginStart;
contentManagement: ContentManagementPublicStart;
+ serverless?: ServerlessPluginStart;
}
/**
@@ -327,6 +329,7 @@ export class VisualizationsPlugin
visEditorsRegistry,
listingViewRegistry,
unifiedSearch: pluginsStart.unifiedSearch,
+ serverless: pluginsStart.serverless,
};
params.element.classList.add('visAppWrapper');
diff --git a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx
index e169a7ebaa034..5c31c08f46853 100644
--- a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx
+++ b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx
@@ -270,6 +270,7 @@ export const VisualizeListing = () => {
uiSettings,
kbnUrlStateStorage,
listingViewRegistry,
+ serverless,
},
} = useKibana();
const { pathname } = useLocation();
@@ -298,13 +299,20 @@ export const VisualizeListing = () => {
useMount(() => {
// Reset editor state for all apps if the visualize listing page is loaded.
stateTransferService.clearEditorState();
- chrome.setBreadcrumbs([
- {
- text: i18n.translate('visualizations.visualizeListingBreadcrumbsTitle', {
- defaultMessage: 'Visualize Library',
- }),
- },
- ]);
+ if (serverless?.setBreadcrumbs) {
+ // reset any deeper context breadcrumbs
+ // "Visualization" breadcrumb is set automatically by the serverless navigation
+ serverless.setBreadcrumbs([]);
+ } else {
+ chrome.setBreadcrumbs([
+ {
+ text: i18n.translate('visualizations.visualizeListingBreadcrumbsTitle', {
+ defaultMessage: 'Visualize Library',
+ }),
+ },
+ ]);
+ }
+
chrome.docTitle.change(
i18n.translate('visualizations.listingPageTitle', { defaultMessage: 'Visualize Library' })
);
diff --git a/src/plugins/visualizations/public/visualize_app/types.ts b/src/plugins/visualizations/public/visualize_app/types.ts
index 8d86648e9d685..90806f138f9b6 100644
--- a/src/plugins/visualizations/public/visualize_app/types.ts
+++ b/src/plugins/visualizations/public/visualize_app/types.ts
@@ -40,6 +40,7 @@ import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/
import type { SpacesPluginStart } from '@kbn/spaces-plugin/public';
import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public';
import type { SavedSearch, SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public';
+import type { ServerlessPluginStart } from '@kbn/serverless/public';
import type {
Vis,
VisualizeEmbeddableContract,
@@ -115,6 +116,7 @@ export interface VisualizeServices extends CoreStart {
visEditorsRegistry: VisEditorsRegistry;
listingViewRegistry: ListingViewRegistry;
unifiedSearch: UnifiedSearchPublicPluginStart;
+ serverless?: ServerlessPluginStart;
}
export interface VisInstance {
diff --git a/src/plugins/visualizations/public/visualize_app/utils/breadcrumbs.ts b/src/plugins/visualizations/public/visualize_app/utils/breadcrumbs.ts
index 5c89e96b1daff..dbb8226bd70b7 100644
--- a/src/plugins/visualizations/public/visualize_app/utils/breadcrumbs.ts
+++ b/src/plugins/visualizations/public/visualize_app/utils/breadcrumbs.ts
@@ -45,6 +45,28 @@ export function getCreateBreadcrumbs({
];
}
+export function getCreateServerlessBreadcrumbs({
+ byValue,
+ originatingAppName,
+ redirectToOrigin,
+}: {
+ byValue?: boolean;
+ originatingAppName?: string;
+ redirectToOrigin?: () => void;
+}) {
+ // TODO: https://github.com/elastic/kibana/issues/163488
+ // for now, serverless breadcrumbs only set the title,
+ // the rest of the breadcrumbs are handled by the serverless navigation
+ // the serverless navigation is not yet aware of the byValue/originatingApp context
+ return [
+ {
+ text: i18n.translate('visualizations.editor.createBreadcrumb', {
+ defaultMessage: 'Create',
+ }),
+ },
+ ];
+}
+
export function getEditBreadcrumbs(
{
byValue,
@@ -65,3 +87,26 @@ export function getEditBreadcrumbs(
},
];
}
+
+export function getEditServerlessBreadcrumbs(
+ {
+ byValue,
+ originatingAppName,
+ redirectToOrigin,
+ }: {
+ byValue?: boolean;
+ originatingAppName?: string;
+ redirectToOrigin?: () => void;
+ },
+ title: string = defaultEditText
+) {
+ // TODO: https://github.com/elastic/kibana/issues/163488
+ // for now, serverless breadcrumbs only set the title,
+ // the rest of the breadcrumbs are handled by the serverless navigation
+ // the serverless navigation is not yet aware of the byValue/originatingApp context
+ return [
+ {
+ text: title,
+ },
+ ];
+}
diff --git a/src/plugins/visualizations/public/visualize_app/utils/get_top_nav_config.tsx b/src/plugins/visualizations/public/visualize_app/utils/get_top_nav_config.tsx
index 9bc781f464084..49ebc45833376 100644
--- a/src/plugins/visualizations/public/visualize_app/utils/get_top_nav_config.tsx
+++ b/src/plugins/visualizations/public/visualize_app/utils/get_top_nav_config.tsx
@@ -36,7 +36,7 @@ import {
VisualizeEditorVisInstance,
} from '../types';
import { VisualizeConstants } from '../../../common/constants';
-import { getEditBreadcrumbs } from './breadcrumbs';
+import { getEditBreadcrumbs, getEditServerlessBreadcrumbs } from './breadcrumbs';
import { VISUALIZE_APP_LOCATOR, VisualizeLocatorParams } from '../../../common/locator';
import { getUiActions } from '../../services';
import { VISUALIZE_EDITOR_TRIGGER, AGG_BASED_VISUALIZATION_TRIGGER } from '../../triggers';
@@ -117,6 +117,7 @@ export const getTopNavConfig = (
savedObjectsTagging,
presentationUtil,
getKibanaVersion,
+ serverless,
}: VisualizeServices
) => {
const { vis, embeddableHandler } = visInstance;
@@ -202,7 +203,11 @@ export const getTopNavConfig = (
stateTransfer.clearEditorState(VisualizeConstants.APP_ID);
}
chrome.docTitle.change(savedVis.lastSavedTitle);
- chrome.setBreadcrumbs(getEditBreadcrumbs({}, savedVis.lastSavedTitle));
+ if (serverless?.setBreadcrumbs) {
+ serverless.setBreadcrumbs(getEditServerlessBreadcrumbs({}, savedVis.lastSavedTitle));
+ } else {
+ chrome.setBreadcrumbs(getEditBreadcrumbs({}, savedVis.lastSavedTitle));
+ }
if (id !== visualizationIdFromUrl) {
history.replace({
diff --git a/src/plugins/visualizations/public/visualize_app/utils/use/use_saved_vis_instance.ts b/src/plugins/visualizations/public/visualize_app/utils/use/use_saved_vis_instance.ts
index dcd53feb5b1e9..8b549ea385822 100644
--- a/src/plugins/visualizations/public/visualize_app/utils/use/use_saved_vis_instance.ts
+++ b/src/plugins/visualizations/public/visualize_app/utils/use/use_saved_vis_instance.ts
@@ -12,7 +12,12 @@ import { parse } from 'query-string';
import { i18n } from '@kbn/i18n';
import { getVisualizationInstance } from '../get_visualization_instance';
-import { getEditBreadcrumbs, getCreateBreadcrumbs } from '../breadcrumbs';
+import {
+ getEditBreadcrumbs,
+ getCreateBreadcrumbs,
+ getCreateServerlessBreadcrumbs,
+ getEditServerlessBreadcrumbs,
+} from '../breadcrumbs';
import { SavedVisInstance, VisualizeServices, IEditorController } from '../../types';
import { VisualizeConstants } from '../../../../common/constants';
import { getTypes } from '../../../services';
@@ -46,6 +51,7 @@ export const useSavedVisInstance = (
stateTransferService,
visEditorsRegistry,
application: { navigateToApp },
+ serverless,
} = services;
const getSavedVisInstance = async () => {
try {
@@ -104,18 +110,35 @@ export const useSavedVisInstance = (
const redirectToOrigin = originatingApp ? () => navigateToApp(originatingApp) : undefined;
if (savedVis.id) {
- chrome.setBreadcrumbs(
- getEditBreadcrumbs({ originatingAppName, redirectToOrigin }, savedVis.title)
- );
+ if (serverless?.setBreadcrumbs) {
+ serverless.setBreadcrumbs(
+ getEditServerlessBreadcrumbs({ originatingAppName, redirectToOrigin }, savedVis.title)
+ );
+ } else {
+ chrome.setBreadcrumbs(
+ getEditBreadcrumbs({ originatingAppName, redirectToOrigin }, savedVis.title)
+ );
+ }
+
chrome.docTitle.change(savedVis.title);
} else {
- chrome.setBreadcrumbs(
- getCreateBreadcrumbs({
- byValue: Boolean(originatingApp),
- originatingAppName,
- redirectToOrigin,
- })
- );
+ if (serverless?.setBreadcrumbs) {
+ serverless.setBreadcrumbs(
+ getCreateServerlessBreadcrumbs({
+ byValue: Boolean(originatingApp),
+ originatingAppName,
+ redirectToOrigin,
+ })
+ );
+ } else {
+ chrome.setBreadcrumbs(
+ getCreateBreadcrumbs({
+ byValue: Boolean(originatingApp),
+ originatingAppName,
+ redirectToOrigin,
+ })
+ );
+ }
}
let visEditorController;
diff --git a/src/plugins/visualizations/public/visualize_app/utils/use/use_vis_byvalue.ts b/src/plugins/visualizations/public/visualize_app/utils/use/use_vis_byvalue.ts
index 37495fea848cd..15329792746e1 100644
--- a/src/plugins/visualizations/public/visualize_app/utils/use/use_vis_byvalue.ts
+++ b/src/plugins/visualizations/public/visualize_app/utils/use/use_vis_byvalue.ts
@@ -11,7 +11,7 @@ import { useEffect, useRef, useState } from 'react';
import { VisualizeInput } from '../../..';
import { ByValueVisInstance, VisualizeServices, IEditorController } from '../../types';
import { getVisualizationInstanceFromInput } from '../get_visualization_instance';
-import { getEditBreadcrumbs } from '../breadcrumbs';
+import { getEditBreadcrumbs, getEditServerlessBreadcrumbs } from '../breadcrumbs';
export const useVisByValue = (
services: VisualizeServices,
@@ -33,6 +33,7 @@ export const useVisByValue = (
application: { navigateToApp },
stateTransferService,
visEditorsRegistry,
+ serverless,
} = services;
const getVisInstance = async () => {
if (!valueInput || loaded.current || !visEditorRef.current) {
@@ -59,9 +60,16 @@ export const useVisByValue = (
const redirectToOrigin = originatingApp
? () => navigateToApp(originatingApp, { path: originatingPath })
: undefined;
- chrome?.setBreadcrumbs(
- getEditBreadcrumbs({ byValue: true, originatingAppName, redirectToOrigin })
- );
+
+ if (serverless?.setBreadcrumbs) {
+ serverless.setBreadcrumbs(
+ getEditServerlessBreadcrumbs({ byValue: true, originatingAppName, redirectToOrigin })
+ );
+ } else {
+ chrome?.setBreadcrumbs(
+ getEditBreadcrumbs({ byValue: true, originatingAppName, redirectToOrigin })
+ );
+ }
loaded.current = true;
setState({
diff --git a/src/plugins/visualizations/tsconfig.json b/src/plugins/visualizations/tsconfig.json
index e4c302c17a43b..c72d4fd24d7ea 100644
--- a/src/plugins/visualizations/tsconfig.json
+++ b/src/plugins/visualizations/tsconfig.json
@@ -61,7 +61,8 @@
"@kbn/content-management-table-list-view-table",
"@kbn/content-management-tabbed-table-list-view",
"@kbn/content-management-table-list-view",
- "@kbn/content-management-utils"
+ "@kbn/content-management-utils",
+ "@kbn/serverless"
],
"exclude": [
"target/**/*",
diff --git a/x-pack/plugins/lens/kibana.jsonc b/x-pack/plugins/lens/kibana.jsonc
index d7e32fba863fe..04bb96af59388 100644
--- a/x-pack/plugins/lens/kibana.jsonc
+++ b/x-pack/plugins/lens/kibana.jsonc
@@ -45,7 +45,8 @@
"taskManager",
"globalSearch",
"savedObjectsTagging",
- "spaces"
+ "spaces",
+ "serverless"
],
"requiredBundles": [
"unifiedSearch",
diff --git a/x-pack/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/plugins/lens/public/app_plugin/app.test.tsx
index c94cf2ca12eae..2cd90ad5b99b1 100644
--- a/x-pack/plugins/lens/public/app_plugin/app.test.tsx
+++ b/x-pack/plugins/lens/public/app_plugin/app.test.tsx
@@ -33,6 +33,7 @@ import { TopNavMenuData } from '@kbn/navigation-plugin/public';
import { LensByValueInput } from '../embeddable/embeddable';
import { SavedObjectReference } from '@kbn/core/types';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
+import { serverlessMock } from '@kbn/serverless/public/mocks';
import moment from 'moment';
import { setState, LensAppState } from '../state_management';
@@ -365,6 +366,30 @@ describe('Lens App', () => {
{ text: 'Daaaaaaadaumching!' },
]);
});
+
+ it('sets serverless breadcrumbs when the document title changes when serverless service is available', async () => {
+ const serverless = serverlessMock.createStart();
+ const { instance, services, lensStore } = await mountWith({
+ services: {
+ ...makeDefaultServices(),
+ serverless,
+ },
+ });
+ expect(services.chrome.setBreadcrumbs).not.toHaveBeenCalled();
+ expect(serverless.setBreadcrumbs).toHaveBeenCalledWith({ text: 'Create' });
+
+ await act(async () => {
+ instance.setProps({ initialInput: { savedObjectId: breadcrumbDocSavedObjectId } });
+ lensStore.dispatch(
+ setState({
+ persistedDoc: breadcrumbDoc,
+ })
+ );
+ });
+
+ expect(services.chrome.setBreadcrumbs).not.toHaveBeenCalled();
+ expect(serverless.setBreadcrumbs).toHaveBeenCalledWith({ text: 'Daaaaaaadaumching!' });
+ });
});
describe('TopNavMenu#showDatePicker', () => {
diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx
index 174c2307424a3..22c5a21ad3377 100644
--- a/x-pack/plugins/lens/public/app_plugin/app.tsx
+++ b/x-pack/plugins/lens/public/app_plugin/app.tsx
@@ -93,6 +93,7 @@ export function App({
dashboardFeatureFlag,
locator,
share,
+ serverless,
} = lensAppServices;
const saveAndExit = useRef<() => void>();
@@ -288,8 +289,18 @@ export function App({
},
});
}
- breadcrumbs.push({ text: currentDocTitle });
- chrome.setBreadcrumbs(breadcrumbs);
+
+ const currentDocBreadcrumb: EuiBreadcrumb = { text: currentDocTitle };
+ breadcrumbs.push(currentDocBreadcrumb);
+ if (serverless?.setBreadcrumbs) {
+ // TODO: https://github.com/elastic/kibana/issues/163488
+ // for now, serverless breadcrumbs only set the title,
+ // the rest of the breadcrumbs are handled by the serverless navigation
+ // the serverless navigation is not yet aware of the byValue/originatingApp context
+ serverless.setBreadcrumbs(currentDocBreadcrumb);
+ } else {
+ chrome.setBreadcrumbs(breadcrumbs);
+ }
}, [
dashboardFeatureFlag.allowByValueEmbeddables,
getOriginatingAppName,
@@ -300,6 +311,7 @@ export function App({
isLinkedToOriginatingApp,
persistedDoc,
initialContext,
+ serverless,
]);
const switchDatasource = useCallback(() => {
diff --git a/x-pack/plugins/lens/public/app_plugin/mounter.tsx b/x-pack/plugins/lens/public/app_plugin/mounter.tsx
index 0742e48882748..3f6009659199c 100644
--- a/x-pack/plugins/lens/public/app_plugin/mounter.tsx
+++ b/x-pack/plugins/lens/public/app_plugin/mounter.tsx
@@ -101,6 +101,7 @@ export async function getLensServices(
spaces,
share,
unifiedSearch,
+ serverless,
} = startDependencies;
const storage = new Storage(localStorage);
@@ -147,6 +148,7 @@ export async function getLensServices(
unifiedSearch,
docLinks: coreStart.docLinks,
locator,
+ serverless,
};
}
diff --git a/x-pack/plugins/lens/public/app_plugin/types.ts b/x-pack/plugins/lens/public/app_plugin/types.ts
index 142d24f645465..263794db96c87 100644
--- a/x-pack/plugins/lens/public/app_plugin/types.ts
+++ b/x-pack/plugins/lens/public/app_plugin/types.ts
@@ -47,6 +47,7 @@ import type { DocLinksStart } from '@kbn/core-doc-links-browser';
import type { SharePluginStart } from '@kbn/share-plugin/public';
import type { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public';
import type { SettingsStart } from '@kbn/core-ui-settings-browser';
+import type { ServerlessPluginStart } from '@kbn/serverless/public';
import type {
DatasourceMap,
EditorFrameInstance,
@@ -174,6 +175,7 @@ export interface LensAppServices {
dataViewFieldEditor: IndexPatternFieldEditorStart;
locator?: LensAppLocator;
savedObjectStore: SavedObjectIndexStore;
+ serverless?: ServerlessPluginStart;
}
interface TopNavAction {
diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts
index 14cd471a2a5bc..957422da5d6a4 100644
--- a/x-pack/plugins/lens/public/plugin.ts
+++ b/x-pack/plugins/lens/public/plugin.ts
@@ -63,6 +63,7 @@ import {
ContentManagementPublicStart,
} from '@kbn/content-management-plugin/public';
import { i18n } from '@kbn/i18n';
+import type { ServerlessPluginStart } from '@kbn/serverless/public';
import type { EditorFrameService as EditorFrameServiceType } from './editor_frame_service';
import type {
FormBasedDatasource as FormBasedDatasourceType,
@@ -168,6 +169,7 @@ export interface LensPluginStartDependencies {
share?: SharePluginStart;
eventAnnotationService: EventAnnotationServiceType;
contentManagement: ContentManagementPublicStart;
+ serverless?: ServerlessPluginStart;
}
export interface LensPublicSetup {
diff --git a/x-pack/plugins/lens/tsconfig.json b/x-pack/plugins/lens/tsconfig.json
index 23dfd600e2048..1a197f781a6ad 100644
--- a/x-pack/plugins/lens/tsconfig.json
+++ b/x-pack/plugins/lens/tsconfig.json
@@ -84,6 +84,7 @@
"@kbn/core-theme-browser-mocks",
"@kbn/event-annotation-components",
"@kbn/content-management-utils",
+ "@kbn/serverless",
],
"exclude": [
"target/**/*",
diff --git a/x-pack/plugins/maps/kibana.jsonc b/x-pack/plugins/maps/kibana.jsonc
index 42cbd85998e59..3fb66c4d93151 100644
--- a/x-pack/plugins/maps/kibana.jsonc
+++ b/x-pack/plugins/maps/kibana.jsonc
@@ -41,7 +41,8 @@
"screenshotMode",
"security",
"spaces",
- "usageCollection"
+ "usageCollection",
+ "serverless"
],
"requiredBundles": [
"kibanaReact",
diff --git a/x-pack/plugins/maps/public/kibana_services.ts b/x-pack/plugins/maps/public/kibana_services.ts
index 287000964dc2b..f34a8ca987ecb 100644
--- a/x-pack/plugins/maps/public/kibana_services.ts
+++ b/x-pack/plugins/maps/public/kibana_services.ts
@@ -76,6 +76,7 @@ export const getContentManagement = () => pluginsStart.contentManagement;
export const isScreenshotMode = () => {
return pluginsStart.screenshotMode ? pluginsStart.screenshotMode.isScreenshotMode() : false;
};
+export const getServerless = () => pluginsStart.serverless;
// xpack.maps.* kibana.yml settings from this plugin
let mapAppConfig: MapsConfigType;
diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts
index 2e6d203d05dae..4e654f9e5c641 100644
--- a/x-pack/plugins/maps/public/plugin.ts
+++ b/x-pack/plugins/maps/public/plugin.ts
@@ -45,6 +45,7 @@ import type {
ContentManagementPublicSetup,
ContentManagementPublicStart,
} from '@kbn/content-management-plugin/public';
+import type { ServerlessPluginStart } from '@kbn/serverless/public';
import {
createRegionMapFn,
@@ -121,6 +122,7 @@ export interface MapsPluginStartDependencies {
contentManagement: ContentManagementPublicStart;
screenshotMode?: ScreenshotModePluginSetup;
usageCollection?: UsageCollectionSetup;
+ serverless?: ServerlessPluginStart;
}
/**
diff --git a/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx b/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx
index 93fc858f1c9d8..ea2aefff97b6c 100644
--- a/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx
+++ b/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx
@@ -21,6 +21,7 @@ import {
getNavigateToApp,
getUiSettings,
getUsageCollection,
+ getServerless,
} from '../../kibana_services';
import { mapsClient } from '../../content_management';
@@ -78,7 +79,11 @@ function MapsListViewComp({ history }: Props) {
// wrap chrome updates in useEffect to avoid potentially causing state changes in other component during render phase.
useEffect(() => {
getCoreChrome().docTitle.change(APP_NAME);
- getCoreChrome().setBreadcrumbs([{ text: APP_NAME }]);
+ if (getServerless()) {
+ getServerless()!.setBreadcrumbs({ text: APP_NAME });
+ } else {
+ getCoreChrome().setBreadcrumbs([{ text: APP_NAME }]);
+ }
}, []);
const findMaps = useCallback(
diff --git a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts
index db9cd039071ae..b68905cfac5c2 100644
--- a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts
+++ b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts
@@ -45,6 +45,7 @@ import {
getSavedObjectsTagging,
getTimeFilter,
getUsageCollection,
+ getServerless,
} from '../../../kibana_services';
import { LayerDescriptor } from '../../../../common/descriptor_types';
import { copyPersistentState } from '../../../reducers/copy_persistent_state';
@@ -331,15 +332,23 @@ export class SavedMap {
throw new Error('Invalid usage, must await whenReady before calling hasUnsavedChanges');
}
- const breadcrumbs = getBreadcrumbs({
- pageTitle: this._getPageTitle(),
- isByValue: this.isByValue(),
- getHasUnsavedChanges: this.hasUnsavedChanges,
- originatingApp: this._originatingApp,
- getAppNameFromId: this._getStateTransfer().getAppNameFromId,
- history,
- });
- getCoreChrome().setBreadcrumbs(breadcrumbs);
+ if (getServerless()) {
+ // TODO: https://github.com/elastic/kibana/issues/163488
+ // for now, serverless breadcrumbs only set the title,
+ // the rest of the breadcrumbs are handled by the serverless navigation
+ // the serverless navigation is not yet aware of the byValue/originatingApp context
+ getServerless()!.setBreadcrumbs({ text: this._getPageTitle() });
+ } else {
+ const breadcrumbs = getBreadcrumbs({
+ pageTitle: this._getPageTitle(),
+ isByValue: this.isByValue(),
+ getHasUnsavedChanges: this.hasUnsavedChanges,
+ originatingApp: this._originatingApp,
+ getAppNameFromId: this._getStateTransfer().getAppNameFromId,
+ history,
+ });
+ getCoreChrome().setBreadcrumbs(breadcrumbs);
+ }
}
public getSavedObjectId(): string | undefined {
diff --git a/x-pack/plugins/maps/tsconfig.json b/x-pack/plugins/maps/tsconfig.json
index b0e27ee323dba..34066a8b8d538 100644
--- a/x-pack/plugins/maps/tsconfig.json
+++ b/x-pack/plugins/maps/tsconfig.json
@@ -72,6 +72,7 @@
"@kbn/core-http-common",
"@kbn/content-management-table-list-view-table",
"@kbn/content-management-table-list-view",
+ "@kbn/serverless",
],
"exclude": [
"target/**/*",
From 42d4b6f0091383ac7bae1a6d3869c7ca85181759 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Fri, 11 Aug 2023 02:16:46 -0700
Subject: [PATCH 41/45] Update APM (main) (#163623)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
package.json | 4 ++--
yarn.lock | 28 ++++++++++++++--------------
2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/package.json b/package.json
index 9f93dce31d0fa..27016cdefdb0b 100644
--- a/package.json
+++ b/package.json
@@ -92,8 +92,8 @@
"@dnd-kit/core": "^3.1.1",
"@dnd-kit/sortable": "^4.0.0",
"@dnd-kit/utilities": "^2.0.0",
- "@elastic/apm-rum": "^5.13.0",
- "@elastic/apm-rum-react": "^1.4.3",
+ "@elastic/apm-rum": "^5.14.0",
+ "@elastic/apm-rum-react": "^1.4.4",
"@elastic/charts": "59.1.0",
"@elastic/datemath": "5.0.3",
"@elastic/elasticsearch": "npm:@elastic/elasticsearch@8.9.0",
diff --git a/yarn.lock b/yarn.lock
index 93eef32dcd84e..945c81f6d566d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1456,29 +1456,29 @@
dependencies:
tslib "^2.0.0"
-"@elastic/apm-rum-core@^5.18.0":
- version "5.18.0"
- resolved "https://registry.yarnpkg.com/@elastic/apm-rum-core/-/apm-rum-core-5.18.0.tgz#d1c25594c8e81a929b1f9444ecc081359a97b3d5"
- integrity sha512-KGU+ZFtdXdD7pR+arDq0JRIC4IKIH0D16p+7SMkq9Lq8wq70Uy6r5SKZgW7X6ahnJP35hStMXT2mjStjD9a+wA==
+"@elastic/apm-rum-core@^5.19.0":
+ version "5.19.0"
+ resolved "https://registry.yarnpkg.com/@elastic/apm-rum-core/-/apm-rum-core-5.19.0.tgz#653a120e60549b2486c86919e5079df9fd779a67"
+ integrity sha512-vjddutdSY2L15I0hFd45PaStleemFfxmvXj1KjiFCbRGQRW2JhMoaNJ6YpFXP+L5rs96olwXGzYLHaztWs1ciQ==
dependencies:
error-stack-parser "^1.3.5"
opentracing "^0.14.3"
promise-polyfill "^8.1.3"
-"@elastic/apm-rum-react@^1.4.3":
- version "1.4.3"
- resolved "https://registry.yarnpkg.com/@elastic/apm-rum-react/-/apm-rum-react-1.4.3.tgz#cbbde9d8e0f9aaf8459e07626d88b15188f99d37"
- integrity sha512-9OSy373fNkLfnf6g8lEBjycm03NJnTPeTYTLUh+Gvlf5NGic+mLZbm7d4dQvUxgIcjKzlx94et3SGysG6JzDrg==
+"@elastic/apm-rum-react@^1.4.4":
+ version "1.4.4"
+ resolved "https://registry.yarnpkg.com/@elastic/apm-rum-react/-/apm-rum-react-1.4.4.tgz#f716d9a5b44e2c8d89b47fb90ad24264c4e67cea"
+ integrity sha512-j6WZSDlA1SsWuAhn9bv2HGXFhoHe3TQVvOysUXdRvCyo2yzzdiwGQeqJs5Gl4dfxqZmyFnlutpAnoygTJVWdtQ==
dependencies:
- "@elastic/apm-rum" "^5.13.0"
+ "@elastic/apm-rum" "^5.14.0"
hoist-non-react-statics "^3.3.0"
-"@elastic/apm-rum@^5.13.0":
- version "5.13.0"
- resolved "https://registry.yarnpkg.com/@elastic/apm-rum/-/apm-rum-5.13.0.tgz#a79df440a2e80a492b24e9c04b62c408a6863fa4"
- integrity sha512-b3H9EEwUpDsGhtqY9oiVILR3nFh8jM9rA9LQGZxw155d2x1MEFyqOhI1uXkhgxirnC+RZHRuQKcfWxZtPQnrfA==
+"@elastic/apm-rum@^5.14.0":
+ version "5.14.0"
+ resolved "https://registry.yarnpkg.com/@elastic/apm-rum/-/apm-rum-5.14.0.tgz#a7d503a3ef3272767e383e9be780bf68a63c553f"
+ integrity sha512-JyJrKAtumXpQL9X3MTkR4YTw7CzYq5O7jqpB7nVZtqgmfkKgUBAQsmQ4kkpIYhDFDKGDI+45EBj+O0ZQ9QND9w==
dependencies:
- "@elastic/apm-rum-core" "^5.18.0"
+ "@elastic/apm-rum-core" "^5.19.0"
"@elastic/app-search-javascript@^8.1.2":
version "8.1.2"
From c8e5741a0b4722d1adb8e532ab2997b30d664fc1 Mon Sep 17 00:00:00 2001
From: Liam Thompson <32779855+leemthompo@users.noreply.github.com>
Date: Fri, 11 Aug 2023 11:27:26 +0200
Subject: [PATCH 42/45] [Enterprise Search] Update Workplace Search connectors
doclink (#163676)
Small URL update.
---
packages/kbn-doc-links/src/get_doc_links.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts
index 7bf1b32195dd5..49105db3e5db4 100644
--- a/packages/kbn-doc-links/src/get_doc_links.ts
+++ b/packages/kbn-doc-links/src/get_doc_links.ts
@@ -152,7 +152,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => {
connectorsServiceNow: `${ENTERPRISE_SEARCH_DOCS}connectors-servicenow.html`,
connectorsSharepoint: `${ENTERPRISE_SEARCH_DOCS}connectors-sharepoint.html`,
connectorsSharepointOnline: `${ENTERPRISE_SEARCH_DOCS}connectors-sharepoint-online.html`,
- connectorsWorkplaceSearch: `${ENTERPRISE_SEARCH_DOCS}connectors.html#connectors-workplace-search`,
+ connectorsWorkplaceSearch: `${ENTERPRISE_SEARCH_DOCS}workplace-search-connectors.html`,
crawlerExtractionRules: `${ENTERPRISE_SEARCH_DOCS}crawler-extraction-rules.html`,
crawlerManaging: `${ENTERPRISE_SEARCH_DOCS}crawler-managing.html`,
crawlerOverview: `${ENTERPRISE_SEARCH_DOCS}crawler.html`,
From 81ca020ad710c500ce7a33b51439c4d413715c3c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20S=C3=A1nchez?=
Date: Fri, 11 Aug 2023 11:45:14 +0200
Subject: [PATCH 43/45] [Security Solution][Endpoint] Removes pMap and uses a
for loop instead (#163509)
## Summary
Removes pMap and uses a for loop instead when fetching exceptions (as it
was done before) since exceptions are cached and there is no need for
parallel code here.
Original PR with previous changes:
https://github.com/elastic/kibana/pull/160387
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../manifest_manager/manifest_manager.ts | 23 +++++--------------
1 file changed, 6 insertions(+), 17 deletions(-)
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts
index e6b15c07262f7..2dd607604b82f 100644
--- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts
@@ -5,7 +5,6 @@
* 2.0.
*/
-import pMap from 'p-map';
import semver from 'semver';
import { isEqual, isEmpty, chunk, keyBy } from 'lodash';
import type { ElasticsearchClient } from '@kbn/core/server';
@@ -223,23 +222,13 @@ export class ManifestManager {
osOptions: BuildArtifactsForOsOptions
): Promise> {
const policySpecificArtifacts: Record = {};
- await pMap(
- allPolicyIds,
- async (policyId) => {
- for (const os of supportedOSs) {
- policySpecificArtifacts[policyId] = policySpecificArtifacts[policyId] || [];
- policySpecificArtifacts[policyId].push(
- await this.buildArtifactsForOs({ os, policyId, ...osOptions })
- );
- }
- },
- {
- concurrency: 5,
- /** When set to false, instead of stopping when a promise rejects, it will wait for all the promises to
- * settle and then reject with an aggregated error containing all the errors from the rejected promises. */
- stopOnError: false,
+ for (const policyId of allPolicyIds)
+ for (const os of supportedOSs) {
+ policySpecificArtifacts[policyId] = policySpecificArtifacts[policyId] || [];
+ policySpecificArtifacts[policyId].push(
+ await this.buildArtifactsForOs({ os, policyId, ...osOptions })
+ );
}
- );
return policySpecificArtifacts;
}
From 4c812e3b6d1a4e477768dae04047e79bbdc940ff Mon Sep 17 00:00:00 2001
From: Marco Liberati
Date: Fri, 11 Aug 2023 11:52:17 +0200
Subject: [PATCH 44/45] [Lens] Relax counter field checks for saved
visualizations with unsupported operations (#163515)
## Summary
Fix #163473
This PR relaxes a bit the checks on the Lens side for old/saved
visualizations with unsupported operations for the `counter` field type,
while preserving those checks for newer visualizations.
Dashboards with "meaningless" operations will now show a warning
message:
When in editor the warning is shown at the top-right corner as well:
New visualizations still prevent the user from using the unsupported
operations:
There's theoretically a last case where users in old SOs might create a
new metric dimension trying to force to use a unsupported operation for
a counter field: in this case the logic for a "new" visualization will
kick-in, clean the data in the workspace and show a full error.
Cancelling such metric dimension will lead to the previous "relaxed"
state.
Messages are grouped by field and by top referencing column (i.e. a
formula): this means that if a formula uses the same `counter` field
with two different dimensions (i.e. `sum(counter_field) +
median(counter_field)` as `myFormula`) will show up as a single column
(`myFormula`).
The wording of the message mimics the same documentation copy provided
in the ES documentation. Ref:
https://github.com/elastic/elasticsearch/pull/97974
Testing SO:
[export.ndjson.txt](https://github.com/elastic/kibana/files/12304924/export.ndjson.txt)
### Checklist
Delete any items that are not applicable to this PR.
- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
---------
Co-authored-by: Stratoula Kalafateli
---
.../datasources/form_based/form_based.tsx | 2 +
.../operations/definitions/metrics.tsx | 1 -
.../datasources/form_based/utils.test.tsx | 200 +++++++++++++++++-
.../public/datasources/form_based/utils.tsx | 142 +++++++++++--
4 files changed, 326 insertions(+), 19 deletions(-)
diff --git a/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx b/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx
index f51fe7a65eb14..51ed27f328838 100644
--- a/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx
+++ b/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx
@@ -69,6 +69,7 @@ import {
isColumnInvalid,
cloneLayer,
getNotifiableFeatures,
+ getUnsupportedOperationsWarningMessage,
} from './utils';
import { getUniqueLabelGenerator, isDraggedDataViewField, nonNullable } from '../../utils';
import { hasField, normalizeOperationDataType } from './pure_utils';
@@ -801,6 +802,7 @@ export function getFormBasedDatasource({
core.docLinks,
setState
),
+ ...getUnsupportedOperationsWarningMessage(state, frameDatasourceAPI, core.docLinks),
];
const infoMessages = getNotifiableFeatures(state, frameDatasourceAPI, visualizationInfo);
diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/metrics.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/metrics.tsx
index deb0c19dc4837..2b9d45979b446 100644
--- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/metrics.tsx
+++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/metrics.tsx
@@ -126,7 +126,6 @@ function buildMetricOperation>({
newField &&
supportedTypes.includes(newField.type) &&
newField.aggregatable &&
- isTimeSeriesCompatible(type, newField.timeSeriesMetric) &&
(!newField.aggregationRestrictions || newField.aggregationRestrictions![type])
);
},
diff --git a/x-pack/plugins/lens/public/datasources/form_based/utils.test.tsx b/x-pack/plugins/lens/public/datasources/form_based/utils.test.tsx
index ca7f346033111..f4aa785bd25f2 100644
--- a/x-pack/plugins/lens/public/datasources/form_based/utils.test.tsx
+++ b/x-pack/plugins/lens/public/datasources/form_based/utils.test.tsx
@@ -8,15 +8,20 @@
import React from 'react';
import { shallow } from 'enzyme';
import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks';
-import { getPrecisionErrorWarningMessages, cloneLayer } from './utils';
+import {
+ getPrecisionErrorWarningMessages,
+ cloneLayer,
+ getUnsupportedOperationsWarningMessage,
+} from './utils';
import type { FormBasedPrivateState, GenericIndexPatternColumn } from './types';
-import type { FramePublicAPI } from '../../types';
+import type { FramePublicAPI, IndexPattern } from '../../types';
import type { DocLinksStart } from '@kbn/core/public';
import { EuiLink } from '@elastic/eui';
import { TermsIndexPatternColumn } from './operations';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import { FormattedMessage } from '@kbn/i18n-react';
import { FormBasedLayer } from './types';
+import { createMockedIndexPatternWithAdditionalFields } from './mocks';
describe('indexpattern_datasource utils', () => {
describe('getPrecisionErrorWarningMessages', () => {
@@ -240,4 +245,195 @@ describe('indexpattern_datasource utils', () => {
).toMatchSnapshot();
});
});
+
+ describe('getUnsupportedOperationsWarningMessage', () => {
+ let docLinks: DocLinksStart;
+ const affectedOperations = [
+ 'sum',
+ 'average',
+ 'percentile',
+ 'percentile_rank',
+ 'count',
+ 'unique_count',
+ 'standard_deviation',
+ ];
+
+ function createColumnsForField(field: string, colOffset: number = 0) {
+ return Object.fromEntries(
+ affectedOperations.map((operationType, i) => [
+ `col_${i + colOffset}`,
+ { operationType, sourceField: field, label: `${operationType} of ${field}` },
+ ])
+ );
+ }
+
+ function createState(fields: string[]) {
+ return {
+ layers: {
+ id: {
+ indexPatternId: '0',
+ columns: Object.assign(
+ {},
+ ...fields.map((field, i) =>
+ createColumnsForField(field, i * affectedOperations.length)
+ )
+ ),
+ },
+ },
+ } as unknown as FormBasedPrivateState;
+ }
+
+ function createFramePublic(indexPattern: IndexPattern): FramePublicAPI {
+ return {
+ dataViews: {
+ indexPatterns: Object.fromEntries([indexPattern].map((dataView, i) => [i, dataView])),
+ },
+ } as unknown as FramePublicAPI;
+ }
+
+ function createFormulaColumns(formulaParts: string[], field: string, colOffset: number = 0) {
+ const fullFormula = formulaParts.map((part) => `${part}(${field})`).join(' + ');
+ // just assume it's a sum of all the parts for testing
+ const rootId = `col-formula${colOffset}`;
+ return Object.fromEntries([
+ [
+ rootId,
+ {
+ operationType: 'formula',
+ label: `Formula: ${fullFormula}`,
+ params: { formula: fullFormula },
+ },
+ ],
+ ...formulaParts.map((part, i) => [
+ `${rootId}X${i}`,
+ { operationType: part, sourceField: field, label: 'Part of formula' },
+ ]),
+ [
+ `${rootId}X${formulaParts.length}`,
+ { operationType: 'math', references: formulaParts.map((_, i) => `${rootId}X${i}`) },
+ ],
+ ]);
+ }
+
+ beforeEach(() => {
+ docLinks = {
+ links: {
+ fleet: {
+ datastreamsTSDSMetrics: 'http://tsdb_metric_doc',
+ },
+ },
+ } as DocLinksStart;
+ });
+
+ it.each([['bytes'], ['bytes_gauge']])(
+ 'should return no warning for non-counter fields: %s',
+ (fieldName: string) => {
+ const warnings = getUnsupportedOperationsWarningMessage(
+ createState([fieldName]),
+ createFramePublic(
+ createMockedIndexPatternWithAdditionalFields([
+ {
+ name: 'bytes_gauge',
+ displayName: 'bytes_gauge',
+ type: 'number',
+ aggregatable: true,
+ searchable: true,
+ timeSeriesMetric: 'gauge',
+ },
+ ])
+ ),
+ docLinks
+ );
+ expect(warnings).toHaveLength(0);
+ }
+ );
+
+ it('should return a warning for a counter field grouped by field', () => {
+ const warnings = getUnsupportedOperationsWarningMessage(
+ createState(['bytes_counter']),
+ createFramePublic(
+ createMockedIndexPatternWithAdditionalFields([
+ {
+ name: 'bytes_counter',
+ displayName: 'bytes_counter',
+ type: 'number',
+ aggregatable: true,
+ searchable: true,
+ timeSeriesMetric: 'counter',
+ },
+ ])
+ ),
+ docLinks
+ );
+ expect(warnings).toHaveLength(1);
+ });
+
+ it('should group multiple warnings by field', () => {
+ const warnings = getUnsupportedOperationsWarningMessage(
+ createState(['bytes_counter', 'bytes_counter2']),
+ createFramePublic(
+ createMockedIndexPatternWithAdditionalFields([
+ {
+ name: 'bytes_counter',
+ displayName: 'bytes_counter',
+ type: 'number',
+ aggregatable: true,
+ searchable: true,
+ timeSeriesMetric: 'counter',
+ },
+ {
+ name: 'bytes_counter2',
+ displayName: 'bytes_counter2',
+ type: 'number',
+ aggregatable: true,
+ searchable: true,
+ timeSeriesMetric: 'counter',
+ },
+ ])
+ ),
+ docLinks
+ );
+ expect(warnings).toHaveLength(2);
+ });
+
+ it('should handle formula reporting only the top visible dimension', () => {
+ const warnings = getUnsupportedOperationsWarningMessage(
+ {
+ layers: {
+ id: {
+ indexPatternId: '0',
+ columns: Object.assign(
+ {},
+ ...['bytes_counter', 'bytes_counter2'].map((field, i) =>
+ createFormulaColumns(affectedOperations, field, i * affectedOperations.length)
+ )
+ ),
+ },
+ },
+ } as unknown as FormBasedPrivateState,
+ createFramePublic(
+ createMockedIndexPatternWithAdditionalFields([
+ {
+ name: 'bytes_counter',
+ displayName: 'bytes_counter',
+ type: 'number',
+ aggregatable: true,
+ searchable: true,
+ timeSeriesMetric: 'counter',
+ },
+ {
+ name: 'bytes_counter2',
+ displayName: 'bytes_counter2',
+ type: 'number',
+ aggregatable: true,
+ searchable: true,
+ timeSeriesMetric: 'counter',
+ },
+ ])
+ ),
+ docLinks
+ );
+ expect(warnings).toHaveLength(2);
+ });
+ });
});
diff --git a/x-pack/plugins/lens/public/datasources/form_based/utils.tsx b/x-pack/plugins/lens/public/datasources/form_based/utils.tsx
index 50c4b0f78e61b..2d6806902bd52 100644
--- a/x-pack/plugins/lens/public/datasources/form_based/utils.tsx
+++ b/x-pack/plugins/lens/public/datasources/form_based/utils.tsx
@@ -14,7 +14,7 @@ import { TimeRange } from '@kbn/es-query';
import { EuiLink, EuiSpacer, EuiText } from '@elastic/eui';
import type { DatatableColumn } from '@kbn/expressions-plugin/common';
-import { groupBy, escape, uniq } from 'lodash';
+import { groupBy, escape, uniq, uniqBy } from 'lodash';
import type { Query } from '@kbn/data-plugin/common';
import { SearchRequest } from '@kbn/data-plugin/common';
@@ -40,16 +40,19 @@ import type { ReferenceBasedIndexPatternColumn } from './operations/definitions/
import {
operationDefinitionMap,
- GenericIndexPatternColumn,
- TermsIndexPatternColumn,
- CountIndexPatternColumn,
+ getReferenceRoot,
updateColumnParam,
updateDefaultLabels,
- RangeIndexPatternColumn,
- FormulaIndexPatternColumn,
- DateHistogramIndexPatternColumn,
- MaxIndexPatternColumn,
- MinIndexPatternColumn,
+ type GenericIndexPatternColumn,
+ type TermsIndexPatternColumn,
+ type CountIndexPatternColumn,
+ type RangeIndexPatternColumn,
+ type FormulaIndexPatternColumn,
+ type DateHistogramIndexPatternColumn,
+ type MaxIndexPatternColumn,
+ type MinIndexPatternColumn,
+ type GenericOperationDefinition,
+ type FieldBasedIndexPatternColumn,
} from './operations';
import { getInvalidFieldMessage, isColumnOfType } from './operations/definitions/helpers';
@@ -338,6 +341,113 @@ export function getShardFailuresWarningMessages(
return [];
}
+export function getUnsupportedOperationsWarningMessage(
+ state: FormBasedPrivateState,
+ { dataViews }: FramePublicAPI,
+ docLinks: DocLinksStart
+) {
+ const warningMessages: UserMessage[] = [];
+ const columnsWithUnsupportedOperations: Array<
+ [FieldBasedIndexPatternColumn, ReferenceBasedIndexPatternColumn | undefined]
+ > = Object.values(state.layers)
+ // filter layers without dataView loaded yet
+ .filter(({ indexPatternId }) => dataViews.indexPatterns[indexPatternId])
+ .flatMap((layer) => {
+ const dataView = dataViews.indexPatterns[layer.indexPatternId];
+ const columnsEntries = Object.entries(layer.columns);
+ return columnsEntries
+ .filter(([_, column]) => {
+ if (!hasField(column)) {
+ return false;
+ }
+ const field = dataView.getFieldByName(column.sourceField);
+ if (!field) {
+ return false;
+ }
+ return (
+ !(
+ operationDefinitionMap[column.operationType] as Extract<
+ GenericOperationDefinition,
+ { input: 'field' }
+ >
+ ).getPossibleOperationForField(field) && field?.timeSeriesMetric === 'counter'
+ );
+ })
+ .map(
+ ([id, fieldColumn]) =>
+ [fieldColumn, layer.columns[getReferenceRoot(layer, id)]] as [
+ FieldBasedIndexPatternColumn,
+ ReferenceBasedIndexPatternColumn | undefined
+ ]
+ );
+ });
+ if (columnsWithUnsupportedOperations.length) {
+ // group the columns by field
+ // then group together columns of a formula/referenced operation who use the same field
+ const columnsGroupedByField = Object.values(
+ groupBy(columnsWithUnsupportedOperations, ([column]) => column.sourceField)
+ ).map((columnsList) => uniqBy(columnsList, ([column, rootColumn]) => rootColumn ?? column));
+
+ for (const columnsGrouped of columnsGroupedByField) {
+ const sourceField = columnsGrouped[0][0].sourceField;
+ warningMessages.push({
+ severity: 'warning',
+ fixableInEditor: false,
+ displayLocations: [{ id: 'toolbar' }, { id: 'embeddableBadge' }],
+ shortMessage: i18n.translate(
+ 'xpack.lens.indexPattern.tsdbErrorWarning.unsupportedCounterOperationErrorWarning.shortMessage',
+ {
+ defaultMessage:
+ 'The result of {count} {count, plural, one {operation} other {operations}} might be meaningless for {field}: {operations}',
+ values: {
+ count: columnsGrouped.length,
+ operations: columnsGrouped
+ .map(([affectedColumn, rootColumn]) => (rootColumn ?? affectedColumn).label)
+ .join(', '),
+ field: sourceField,
+ },
+ }
+ ),
+ longMessage: (
+ <>
+
+ {columnsGrouped.map(([affectedColumn, rootColumn], i) => (
+
+ {(rootColumn ?? affectedColumn).label}
+ {i < columnsGrouped.length - 1 ? ', ' : ''}
+
+ ))}
+ >
+ ),
+ field: sourceField,
+ link: (
+
+
+
+ ),
+ }}
+ />
+ >
+ ),
+ });
+ }
+ }
+ return warningMessages;
+}
+
export function getPrecisionErrorWarningMessages(
datatableUtilities: DatatableUtilitiesService,
state: FormBasedPrivateState,
@@ -349,18 +459,18 @@ export function getPrecisionErrorWarningMessages(
if (state && activeData) {
Object.entries(activeData)
- .reduce(
- (acc, [layerId, { columns }]) => [
- ...acc,
- ...columns.map((column) => ({ layerId, column })),
- ],
- [] as Array<{ layerId: string; column: DatatableColumn }>
- )
+ .reduce((acc, [layerId, { columns }]) => {
+ acc.push(...columns.map((column) => ({ layerId, column })));
+ return acc;
+ }, [] as Array<{ layerId: string; column: DatatableColumn }>)
.forEach(({ layerId, column }) => {
const currentLayer = state.layers[layerId];
const currentColumn = currentLayer?.columns[column.id];
if (currentLayer && currentColumn && datatableUtilities.hasPrecisionError(column)) {
const indexPattern = dataViews.indexPatterns[currentLayer.indexPatternId];
+ if (!indexPattern) {
+ return;
+ }
// currentColumnIsTerms is mostly a type guard. If there's a precision error,
// we already know that we're dealing with a terms-based operation (at least for now).
const currentColumnIsTerms = isColumnOfType(
From a98dfa1c893ff4c8f39cda7fb3250809089e3124 Mon Sep 17 00:00:00 2001
From: Marco Liberati
Date: Fri, 11 Aug 2023 11:52:31 +0200
Subject: [PATCH 45/45] [Lens] Align decoration color with text color for layer
actions (#163630)
## Summary
Fix #163390
### Checklist
Delete any items that are not applicable to this PR.
- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
### Risk Matrix
Delete this section if it is not applicable to this PR.
Before closing this PR, invite QA, stakeholders, and other developers to
identify risks that should be tested prior to the change/feature
release.
When forming the risk matrix, consider some of the following examples
and how they may potentially impact the change:
| Risk | Probability | Severity | Mitigation/Notes |
|---------------------------|-------------|----------|-------------------------|
| Multiple Spaces—unexpected behavior in non-default Kibana Space.
| Low | High | Integration tests will verify that all features are still
supported in non-default Kibana Space and when user switches between
spaces. |
| Multiple nodes—Elasticsearch polling might have race conditions
when multiple Kibana nodes are polling for the same tasks. | High | Low
| Tasks are idempotent, so executing them multiple times will not result
in logical error, but will degrade performance. To test for this case we
add plenty of unit tests around this logic and document manual testing
procedure. |
| Code should gracefully handle cases when feature X or plugin Y are
disabled. | Medium | High | Unit tests will verify that any feature flag
or plugin combination still results in our service operational. |
| [See more potential risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) |
### 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)
---
.../editor_frame/config_panel/layer_actions/layer_actions.tsx | 3 +++
1 file changed, 3 insertions(+)
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx
index 58ec4bd9ff26a..6c0ebc453c614 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx
@@ -145,6 +145,9 @@ const InContextMenuActions = (props: LayerActionsProps) => {
? {
css: css`
color: ${euiTheme.colors[i.color]};
+ &:hover {
+ text-decoration-color: ${euiTheme.colors[i.color]} !important;
+ }
`,
size: 's', // need to be explicit here as css prop will disable the default small size
}