-
- {i18n.translate('xpack.csp.vulnerabilities.totalVulnerabilities', {
- defaultMessage:
- '{total, plural, one {# Vulnerability} other {# Vulnerabilities}}',
- values: { total: data?.total },
- })}
-
- >
+
+
+
+ {i18n.translate('xpack.csp.vulnerabilities.totalVulnerabilities', {
+ defaultMessage:
+ '{total, plural, one {# Vulnerability} other {# Vulnerabilities}}',
+ values: { total: data?.total },
+ })}
+
+ >
+ ),
+ },
+ right: (
+
+
+
),
},
- right: (
-
-
-
- ),
- },
- }}
- gridStyle={{
- border: 'horizontal',
- cellPadding: 'l',
- stripes: false,
- rowHover: 'none',
- header: 'underline',
- }}
- renderCellValue={renderCellValue}
- inMemory={{ level: 'enhancements' }}
- sorting={{ columns: sort, onSort: onSortHandler }}
- pagination={{
- pageIndex,
- pageSize,
- pageSizeOptions: [10, 25, 100],
- onChangeItemsPerPage,
- onChangePage,
- }}
- />
- {isLastLimitedPage && }
+ }}
+ gridStyle={{
+ border: 'horizontal',
+ cellPadding: 'l',
+ stripes: false,
+ rowHover: 'none',
+ header: 'underline',
+ }}
+ renderCellValue={renderCellValue}
+ inMemory={{ level: 'enhancements' }}
+ sorting={{ columns: sort, onSort: onSortHandler }}
+ pagination={{
+ pageIndex,
+ pageSize,
+ pageSizeOptions: [10, 25, 100],
+ onChangeItemsPerPage,
+ onChangePage,
+ }}
+ virtualizationOptions={{
+ overscanRowCount: 20,
+ }}
+ />
+ {isLastLimitedPage && }
+
{showVulnerabilityFlyout && selectedVulnerability && (
= ({ r
const now = new Date().toISOString();
return [
- {
- '@timestamp': now,
- message: {
- role: MessageRole.System,
- content: `You are logs-gpt, a helpful assistant for logs-based observability. Answer as
- concisely as possible.`,
- },
- },
{
'@timestamp': now,
message: {
diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/processes/process_row.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/processes/process_row.tsx
index 12f6eb066a697..1ec925d9ce148 100644
--- a/x-pack/plugins/infra/public/components/asset_details/tabs/processes/process_row.tsx
+++ b/x-pack/plugins/infra/public/components/asset_details/tabs/processes/process_row.tsx
@@ -45,14 +45,6 @@ export const ContextualInsightProcessRow = ({ command }: { command: string }) =>
}
const now = new Date().toISOString();
return [
- {
- '@timestamp': now,
- message: {
- role: MessageRole.System,
- content: `You are infra-gpt, a helpful assistant for metrics-based infrastructure observability. Answer as
- concisely as possible.`,
- },
- },
{
'@timestamp': now,
message: {
diff --git a/x-pack/plugins/infra/public/containers/plugin_config_context.test.tsx b/x-pack/plugins/infra/public/containers/plugin_config_context.test.tsx
index e3d3c3f28de11..70b3cf466f749 100644
--- a/x-pack/plugins/infra/public/containers/plugin_config_context.test.tsx
+++ b/x-pack/plugins/infra/public/containers/plugin_config_context.test.tsx
@@ -20,9 +20,10 @@ describe('usePluginConfig()', () => {
it('returns the plugin config what was set through the provider', () => {
const config: Partial = {
featureFlags: {
+ customThresholdAlertsEnabled: true,
+ logsUIEnabled: false,
metricsExplorerEnabled: false,
osqueryEnabled: false,
- customThresholdAlertsEnabled: true,
},
};
const { result } = renderHook(() => usePluginConfig(), {
diff --git a/x-pack/plugins/infra/public/plugin.ts b/x-pack/plugins/infra/public/plugin.ts
index fe6c42ab43248..1d08484546506 100644
--- a/x-pack/plugins/infra/public/plugin.ts
+++ b/x-pack/plugins/infra/public/plugin.ts
@@ -85,18 +85,21 @@ export class Plugin implements InfraClientPluginClass {
pluginsSetup.observability.observabilityRuleTypeRegistry.register(
createMetricThresholdRuleType()
);
- pluginsSetup.observability.dashboard.register({
- appName: 'infra_logs',
- hasData: getLogsHasDataFetcher(core.getStartServices),
- fetchData: getLogsOverviewDataFetcher(core.getStartServices),
- });
+
+ if (this.config.featureFlags.logsUIEnabled) {
+ // fetchData `appLink` redirects to logs/stream
+ pluginsSetup.observability.dashboard.register({
+ appName: 'infra_logs',
+ hasData: getLogsHasDataFetcher(core.getStartServices),
+ fetchData: getLogsOverviewDataFetcher(core.getStartServices),
+ });
+ }
pluginsSetup.observability.dashboard.register({
appName: 'infra_metrics',
hasData: createMetricsHasData(core.getStartServices),
fetchData: createMetricsFetchData(core.getStartServices),
});
-
pluginsSetup.logsShared.logViews.setLogViewsStaticConfig({
messageFields: this.config.sources?.default?.fields?.message,
});
@@ -107,13 +110,6 @@ export class Plugin implements InfraClientPluginClass {
]);
/** !! Need to be kept in sync with the deepLinks in x-pack/plugins/infra/public/plugin.ts */
- const infraEntries = [
- { label: 'Inventory', app: 'metrics', path: '/inventory' },
- ...(this.config.featureFlags.metricsExplorerEnabled
- ? [{ label: 'Metrics Explorer', app: 'metrics', path: '/explorer' }]
- : []),
- { label: 'Hosts', isBetaFeature: true, app: 'metrics', path: '/hosts' },
- ];
pluginsSetup.observabilityShared.navigation.registerSections(
startDep$AndHostViewFlag$.pipe(
map(
@@ -136,9 +132,13 @@ export class Plugin implements InfraClientPluginClass {
path: '/',
isBetaFeature: true,
},
- { label: 'Stream', app: 'logs', path: '/stream' },
- { label: 'Anomalies', app: 'logs', path: '/anomalies' },
- { label: 'Categories', app: 'logs', path: '/log-categories' },
+ ...(this.config.featureFlags.logsUIEnabled
+ ? [
+ { label: 'Stream', app: 'logs', path: '/stream' },
+ { label: 'Anomalies', app: 'logs', path: '/anomalies' },
+ { label: 'Categories', app: 'logs', path: '/log-categories' },
+ ]
+ : []),
],
},
]
@@ -148,7 +148,13 @@ export class Plugin implements InfraClientPluginClass {
{
label: 'Infrastructure',
sortKey: 300,
- entries: infraEntries,
+ entries: [
+ { label: 'Inventory', app: 'metrics', path: '/inventory' },
+ ...(this.config.featureFlags.metricsExplorerEnabled
+ ? [{ label: 'Metrics Explorer', app: 'metrics', path: '/explorer' }]
+ : []),
+ { label: 'Hosts', isBetaFeature: true, app: 'metrics', path: '/hosts' },
+ ],
},
]
: []),
@@ -172,54 +178,56 @@ export class Plugin implements InfraClientPluginClass {
createLogThresholdRuleType(core, logsLocator)
);
- core.application.register({
- id: 'logs',
- title: i18n.translate('xpack.infra.logs.pluginTitle', {
- defaultMessage: 'Logs',
- }),
- euiIconType: 'logoObservability',
- order: 8100,
- appRoute: '/app/logs',
- // !! Need to be kept in sync with the routes in x-pack/plugins/infra/public/pages/logs/page_content.tsx
- deepLinks: [
- {
- id: 'stream',
- title: i18n.translate('xpack.infra.logs.index.streamTabTitle', {
- defaultMessage: 'Stream',
- }),
- path: '/stream',
- },
- {
- id: 'anomalies',
- title: i18n.translate('xpack.infra.logs.index.anomaliesTabTitle', {
- defaultMessage: 'Anomalies',
- }),
- path: '/anomalies',
- },
- {
- id: 'log-categories',
- title: i18n.translate('xpack.infra.logs.index.logCategoriesBetaBadgeTitle', {
- defaultMessage: 'Categories',
- }),
- path: '/log-categories',
- },
- {
- id: 'settings',
- title: i18n.translate('xpack.infra.logs.index.settingsTabTitle', {
- defaultMessage: 'Settings',
- }),
- path: '/settings',
+ if (this.config.featureFlags.logsUIEnabled) {
+ core.application.register({
+ id: 'logs',
+ title: i18n.translate('xpack.infra.logs.pluginTitle', {
+ defaultMessage: 'Logs',
+ }),
+ euiIconType: 'logoObservability',
+ order: 8100,
+ appRoute: '/app/logs',
+ // !! Need to be kept in sync with the routes in x-pack/plugins/infra/public/pages/logs/page_content.tsx
+ deepLinks: [
+ {
+ id: 'stream',
+ title: i18n.translate('xpack.infra.logs.index.streamTabTitle', {
+ defaultMessage: 'Stream',
+ }),
+ path: '/stream',
+ },
+ {
+ id: 'anomalies',
+ title: i18n.translate('xpack.infra.logs.index.anomaliesTabTitle', {
+ defaultMessage: 'Anomalies',
+ }),
+ path: '/anomalies',
+ },
+ {
+ id: 'log-categories',
+ title: i18n.translate('xpack.infra.logs.index.logCategoriesBetaBadgeTitle', {
+ defaultMessage: 'Categories',
+ }),
+ path: '/log-categories',
+ },
+ {
+ id: 'settings',
+ title: i18n.translate('xpack.infra.logs.index.settingsTabTitle', {
+ defaultMessage: 'Settings',
+ }),
+ path: '/settings',
+ },
+ ],
+ category: DEFAULT_APP_CATEGORIES.observability,
+ mount: async (params: AppMountParameters) => {
+ // mount callback should not use setup dependencies, get start dependencies instead
+ const [coreStart, plugins, pluginStart] = await core.getStartServices();
+
+ const { renderApp } = await import('./apps/logs_app');
+ return renderApp(coreStart, plugins, pluginStart, params);
},
- ],
- category: DEFAULT_APP_CATEGORIES.observability,
- mount: async (params: AppMountParameters) => {
- // mount callback should not use setup dependencies, get start dependencies instead
- const [coreStart, plugins, pluginStart] = await core.getStartServices();
-
- const { renderApp } = await import('./apps/logs_app');
- return renderApp(coreStart, plugins, pluginStart, params);
- },
- });
+ });
+ }
// !! Need to be kept in sync with the routes in x-pack/plugins/infra/public/pages/metrics/index.tsx
const infraDeepLinks: AppDeepLink[] = [
@@ -285,14 +293,6 @@ export class Plugin implements InfraClientPluginClass {
},
});
- startDep$AndHostViewFlag$.subscribe(
- ([_startServices]: [[CoreStart, InfraClientStartDeps, InfraClientStartExports], boolean]) => {
- this.appUpdater$.next(() => ({
- deepLinks: infraDeepLinks,
- }));
- }
- );
-
/* This exists purely to facilitate URL redirects from the old App ID ("infra"),
to our new App IDs ("metrics" and "logs"). With version 8.0.0 we can remove this. */
core.application.register({
@@ -307,6 +307,14 @@ export class Plugin implements InfraClientPluginClass {
},
});
+ startDep$AndHostViewFlag$.subscribe(
+ ([_startServices]: [[CoreStart, InfraClientStartDeps, InfraClientStartExports], boolean]) => {
+ this.appUpdater$.next(() => ({
+ deepLinks: infraDeepLinks,
+ }));
+ }
+ );
+
// Setup telemetry events
this.telemetry.setup({ analytics: core.analytics });
diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts
index 0752c75b198c3..b3b82602f11f1 100644
--- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts
+++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts
@@ -1899,8 +1899,9 @@ const createMockStaticConfiguration = (sources: any): InfraConfig => ({
compositeSize: 2000,
},
featureFlags: {
- metricsExplorerEnabled: true,
customThresholdAlertsEnabled: false,
+ logsUIEnabled: true,
+ metricsExplorerEnabled: true,
osqueryEnabled: true,
},
enabled: true,
diff --git a/x-pack/plugins/infra/server/lib/sources/sources.test.ts b/x-pack/plugins/infra/server/lib/sources/sources.test.ts
index 53d7b8979de96..d9e3e3ee4dbac 100644
--- a/x-pack/plugins/infra/server/lib/sources/sources.test.ts
+++ b/x-pack/plugins/infra/server/lib/sources/sources.test.ts
@@ -126,8 +126,9 @@ const createMockStaticConfiguration = (sources: any): InfraConfig => ({
compositeSize: 2000,
},
featureFlags: {
- metricsExplorerEnabled: true,
customThresholdAlertsEnabled: false,
+ logsUIEnabled: true,
+ metricsExplorerEnabled: true,
osqueryEnabled: true,
},
sources,
diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts
index 4aeb0971dde8d..04a57f294303f 100644
--- a/x-pack/plugins/infra/server/plugin.ts
+++ b/x-pack/plugins/infra/server/plugin.ts
@@ -81,14 +81,18 @@ export const config: PluginConfigDescriptor = {
})
),
featureFlags: schema.object({
- metricsExplorerEnabled: offeringBasedSchema({
- traditional: schema.boolean({ defaultValue: true }),
- serverless: schema.boolean({ defaultValue: false }),
- }),
customThresholdAlertsEnabled: offeringBasedSchema({
traditional: schema.boolean({ defaultValue: false }),
serverless: schema.boolean({ defaultValue: true }),
}),
+ logsUIEnabled: offeringBasedSchema({
+ traditional: schema.boolean({ defaultValue: true }),
+ serverless: schema.boolean({ defaultValue: false }),
+ }),
+ metricsExplorerEnabled: offeringBasedSchema({
+ traditional: schema.boolean({ defaultValue: true }),
+ serverless: schema.boolean({ defaultValue: false }),
+ }),
osqueryEnabled: offeringBasedSchema({
traditional: schema.boolean({ defaultValue: true }),
serverless: schema.boolean({ defaultValue: false }),
@@ -222,14 +226,16 @@ export class InfraServerPlugin
countLogs: () => UsageCollector.countLogs(),
});
- plugins.home.sampleData.addAppLinksToSampleDataset('logs', [
- {
- sampleObject: null, // indicates that there is no sample object associated with this app link's path
- getPath: () => `/app/logs`,
- label: logsSampleDataLinkLabel,
- icon: 'logsApp',
- },
- ]);
+ if (this.config.featureFlags.logsUIEnabled) {
+ plugins.home.sampleData.addAppLinksToSampleDataset('logs', [
+ {
+ sampleObject: null, // indicates that there is no sample object associated with this app link's path
+ getPath: () => `/app/logs`,
+ label: logsSampleDataLinkLabel,
+ icon: 'logsApp',
+ },
+ ]);
+ }
initInfraServer(this.libs);
registerRuleTypes(plugins.alerting, this.libs, plugins.ml);
diff --git a/x-pack/plugins/infra/server/routes/log_alerts/chart_preview_data.ts b/x-pack/plugins/infra/server/routes/log_alerts/chart_preview_data.ts
index 1c6be6bf56c28..05b4452dc5557 100644
--- a/x-pack/plugins/infra/server/routes/log_alerts/chart_preview_data.ts
+++ b/x-pack/plugins/infra/server/routes/log_alerts/chart_preview_data.ts
@@ -16,6 +16,11 @@ export const initGetLogAlertsChartPreviewDataRoute = ({
framework,
getStartServices,
}: Pick) => {
+ // Replace with the corresponding logs alert rule feature flag
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
+
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies.ts
index e48292a12e95b..30a9aadda432a 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies.ts
@@ -16,6 +16,9 @@ import { getLogEntryAnomalies } from '../../../lib/log_analysis';
import { isMlPrivilegesError } from '../../../lib/log_analysis/errors';
export const initGetLogEntryAnomaliesRoute = ({ framework }: InfraBackendLibs) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies_datasets.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies_datasets.ts
index 3151c6ac82b4e..ce10ba09a059f 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies_datasets.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies_datasets.ts
@@ -15,6 +15,9 @@ import { assertHasInfraMlPlugins } from '../../../utils/request_context';
import { isMlPrivilegesError } from '../../../lib/log_analysis/errors';
export const initGetLogEntryAnomaliesDatasetsRoute = ({ framework }: InfraBackendLibs) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts
index 25994b7d14a20..f51f81a846bbb 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts
@@ -15,6 +15,9 @@ import { assertHasInfraMlPlugins } from '../../../utils/request_context';
import { isMlPrivilegesError } from '../../../lib/log_analysis/errors';
export const initGetLogEntryCategoriesRoute = ({ framework }: InfraBackendLibs) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts
index ba9e389b4a48e..9ed89f1adb05b 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts
@@ -15,6 +15,9 @@ import { assertHasInfraMlPlugins } from '../../../utils/request_context';
import { isMlPrivilegesError } from '../../../lib/log_analysis/errors';
export const initGetLogEntryCategoryDatasetsRoute = ({ framework }: InfraBackendLibs) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets_stats.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets_stats.ts
index ec8589416efb4..a3ea9356a4ac3 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets_stats.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets_stats.ts
@@ -15,6 +15,9 @@ import { isMlPrivilegesError } from '../../../lib/log_analysis/errors';
import { assertHasInfraMlPlugins } from '../../../utils/request_context';
export const initGetLogEntryCategoryDatasetsStatsRoute = ({ framework }: InfraBackendLibs) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts
index 5e9a57768828c..828912143d412 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts
@@ -18,6 +18,9 @@ export const initGetLogEntryCategoryExamplesRoute = ({
framework,
getStartServices,
}: Pick) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_examples.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_examples.ts
index 8b3b2f0449c58..df79783a56edc 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_examples.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_examples.ts
@@ -18,6 +18,9 @@ export const initGetLogEntryExamplesRoute = ({
framework,
getStartServices,
}: Pick) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/validation/datasets.ts b/x-pack/plugins/infra/server/routes/log_analysis/validation/datasets.ts
index f76fd3a5173cf..00ad8b951edd4 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/validation/datasets.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/validation/datasets.ts
@@ -17,6 +17,9 @@ export const initValidateLogAnalysisDatasetsRoute = ({
framework,
logEntries,
}: InfraBackendLibs) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts b/x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts
index 06ebd4d98b0f8..096ea2e7dd8dd 100644
--- a/x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts
+++ b/x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts
@@ -19,6 +19,10 @@ import { logAnalysisValidationV1 } from '../../../../common/http_api';
const escapeHatch = schema.object({}, { unknowns: 'allow' });
export const initValidateLogAnalysisIndicesRoute = ({ framework }: InfraBackendLibs) => {
+ if (!framework.config.featureFlags.logsUIEnabled) {
+ return;
+ }
+
framework
.registerVersionedRoute({
access: 'internal',
diff --git a/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
index 5d0c6123d4dca..2573ef02aaf01 100644
--- a/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
+++ b/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
@@ -39,12 +39,6 @@ import { DataSearchProgress } from '../../data_search_progress';
import { LogEntryActionsMenu } from './log_entry_actions_menu';
import { LogEntryFieldsTable } from './log_entry_fields_table';
-const LOGS_SYSTEM_MESSAGE = {
- content: `You are logs-gpt, a helpful assistant for logs-based observability. Answer as
- concisely as possible.`,
- role: MessageRole.System,
-};
-
export interface LogEntryFlyoutProps {
logEntryId: string | null | undefined;
onCloseFlyout: () => void;
@@ -144,10 +138,6 @@ export const LogEntryFlyout = ({
const now = new Date().toISOString();
return [
- {
- '@timestamp': now,
- message: LOGS_SYSTEM_MESSAGE,
- },
{
'@timestamp': now,
message: {
@@ -170,10 +160,6 @@ export const LogEntryFlyout = ({
const message = logEntry.fields.find((field) => field.field === 'message')?.value[0];
return [
- {
- '@timestamp': now,
- message: LOGS_SYSTEM_MESSAGE,
- },
{
'@timestamp': now,
message: {
diff --git a/x-pack/plugins/ml/server/models/results_service/anomaly_charts.ts b/x-pack/plugins/ml/server/models/results_service/anomaly_charts.ts
index a1247dbb154a9..707a594d5eff3 100644
--- a/x-pack/plugins/ml/server/models/results_service/anomaly_charts.ts
+++ b/x-pack/plugins/ml/server/models/results_service/anomaly_charts.ts
@@ -526,9 +526,9 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu
// TODO - work out how best to display results from detectors with just an over field.
const firstFieldName =
- record.partition_field_name || record.by_field_name || record.over_field_name;
+ record.partition_field_name ?? record.by_field_name ?? record.over_field_name;
const firstFieldValue =
- record.partition_field_value || record.by_field_value || record.over_field_value;
+ record.partition_field_value ?? record.by_field_value ?? record.over_field_value;
if (firstFieldName !== undefined && firstFieldValue !== undefined) {
const groupsForDetector = detectorsForJob[detectorIndex];
@@ -544,7 +544,7 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu
let isSecondSplit = false;
if (record.partition_field_name !== undefined) {
- const splitFieldName = record.over_field_name || record.by_field_name;
+ const splitFieldName = record.over_field_name ?? record.by_field_name;
if (splitFieldName !== undefined) {
isSecondSplit = true;
}
@@ -562,8 +562,8 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu
}
} else {
// Aggregate another level for the over or by field.
- const secondFieldName = record.over_field_name || record.by_field_name;
- const secondFieldValue = record.over_field_value || record.by_field_value;
+ const secondFieldName = record.over_field_name ?? record.by_field_name;
+ const secondFieldValue = record.over_field_value ?? record.by_field_value;
if (secondFieldName !== undefined && secondFieldValue !== undefined) {
if (dataForGroupValue[secondFieldName] === undefined) {
@@ -1044,7 +1044,7 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu
let chartData: ChartPoint[] = [];
if (metricData !== undefined) {
if (records.length > 0) {
- const filterField = records[0].by_field_value || records[0].over_field_value;
+ const filterField = records[0].by_field_value ?? records[0].over_field_value;
if (eventDistribution && eventDistribution.length > 0) {
chartData = eventDistribution.filter((d: { entity: any }) => d.entity !== filterField);
}
@@ -1143,7 +1143,7 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu
chartType === CHART_TYPE.POPULATION_DISTRIBUTION
) {
return chartData.filter((d) => {
- return d.entity === (record && (record.by_field_value || record.over_field_value));
+ return d.entity === (record && (record.by_field_value ?? record.over_field_value));
});
}
diff --git a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx
index 2b56523d1e879..a48d53c942055 100644
--- a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx
+++ b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx
@@ -4,11 +4,11 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { last } from 'lodash';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { AbortError } from '@kbn/kibana-utils-plugin/common';
-import type { Subscription } from 'rxjs';
+import { isObservable, Subscription } from 'rxjs';
import { MessageRole, type Message } from '../../../common/types';
import { ObservabilityAIAssistantChatServiceProvider } from '../../context/observability_ai_assistant_chat_service_provider';
import { useKibana } from '../../hooks/use_kibana';
@@ -41,6 +41,9 @@ function ChatContent({
const chatService = useObservabilityAIAssistantChatService();
const [pendingMessage, setPendingMessage] = useState();
+
+ const [recalledMessages, setRecalledMessages] = useState(undefined);
+
const [loading, setLoading] = useState(false);
const [subscription, setSubscription] = useState();
@@ -56,37 +59,101 @@ function ChatContent({
const conversationTitle = conversationId
? conversation.value?.conversation.title || ''
: defaultTitle;
- const reloadReply = useCallback(() => {
+
+ const controllerRef = useRef(new AbortController());
+
+ const reloadRecalledMessages = useCallback(async () => {
setLoading(true);
+ setDisplayedMessages(initialMessages);
+
+ setRecalledMessages(undefined);
+
+ controllerRef.current.abort();
+
+ const controller = (controllerRef.current = new AbortController());
+
+ let appendedMessages: Message[] = [];
+
+ if (chatService.hasFunction('recall')) {
+ try {
+ // manually execute recall function and append to list of
+ // messages
+ const functionCall = {
+ name: 'recall',
+ args: JSON.stringify({ queries: [], contexts: [] }),
+ };
+
+ const response = await chatService.executeFunction({
+ ...functionCall,
+ messages: initialMessages,
+ signal: controller.signal,
+ connectorId,
+ });
+
+ if (isObservable(response)) {
+ throw new Error('Recall function unexpectedly returned an Observable');
+ }
+
+ appendedMessages = [
+ {
+ '@timestamp': new Date().toISOString(),
+ message: {
+ role: MessageRole.Assistant,
+ content: '',
+ function_call: {
+ name: functionCall.name,
+ arguments: functionCall.args,
+ trigger: MessageRole.User as const,
+ },
+ },
+ },
+ {
+ '@timestamp': new Date().toISOString(),
+ message: {
+ role: MessageRole.User,
+ name: functionCall.name,
+ content: JSON.stringify(response.content),
+ },
+ },
+ ];
+
+ setRecalledMessages(appendedMessages);
+ } catch (err) {
+ // eslint-disable-next-line no-console
+ console.error(err);
+ setRecalledMessages([]);
+ }
+ }
+ }, [chatService, connectorId, initialMessages, setDisplayedMessages]);
+
+ useEffect(() => {
let lastPendingMessage: PendingMessage | undefined;
+ if (recalledMessages === undefined) {
+ // don't do anything, it's loading
+ return;
+ }
+
const nextSubscription = chatService
- .chat({ messages: initialMessages, connectorId, function: 'none' })
+ .chat({ messages: displayedMessages.concat(recalledMessages), connectorId, function: 'none' })
.subscribe({
next: (msg) => {
lastPendingMessage = msg;
setPendingMessage(() => msg);
},
complete: () => {
- setDisplayedMessages((prevMessages) =>
- prevMessages.concat({
- '@timestamp': new Date().toISOString(),
- message: {
- ...lastPendingMessage!.message,
- },
- })
- );
+ setPendingMessage(lastPendingMessage);
setLoading(false);
},
});
setSubscription(nextSubscription);
- }, [initialMessages, setDisplayedMessages, connectorId, chatService]);
+ }, [chatService, connectorId, displayedMessages, setDisplayedMessages, recalledMessages]);
useEffect(() => {
- reloadReply();
- }, [reloadReply]);
+ reloadRecalledMessages();
+ }, [reloadRecalledMessages]);
useEffect(() => {
setDisplayedMessages(initialMessages);
@@ -96,23 +163,25 @@ function ChatContent({
const messagesWithPending = useMemo(() => {
return pendingMessage
- ? displayedMessages.concat({
+ ? displayedMessages.concat(recalledMessages || []).concat({
'@timestamp': new Date().toISOString(),
message: {
...pendingMessage.message,
},
})
- : displayedMessages;
- }, [pendingMessage, displayedMessages]);
+ : displayedMessages.concat(recalledMessages || []);
+ }, [pendingMessage, displayedMessages, recalledMessages]);
- const lastMessage = last(messagesWithPending);
+ const lastAssistantMessage = last(
+ messagesWithPending.filter((message) => message.message.role === MessageRole.Assistant)
+ );
return (
<>
{}}
/>
@@ -147,7 +216,7 @@ function ChatContent({
{
- reloadReply();
+ reloadRecalledMessages();
}}
/>
@@ -168,7 +237,7 @@ function ChatContent({
onClose={() => {
setIsOpen(() => false);
}}
- messages={displayedMessages}
+ messages={messagesWithPending}
conversationId={conversationId}
startedFrom="contextualInsight"
onChatComplete={(nextMessages) => {
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
index fc65f04c4d116..965a8b899879a 100644
--- a/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversation.ts
+++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversation.ts
@@ -6,9 +6,10 @@
*/
import { i18n } from '@kbn/i18n';
import { merge, omit } from 'lodash';
-import { Dispatch, SetStateAction, useState } from 'react';
+import { Dispatch, SetStateAction, useMemo, useState } from 'react';
import { type Conversation, type Message } from '../../common';
-import type { ConversationCreateRequest } from '../../common/types';
+import { ConversationCreateRequest, MessageRole } from '../../common/types';
+import { getAssistantSetupMessage } from '../service/get_assistant_setup_message';
import { ObservabilityAIAssistantChatService } from '../types';
import { useAbortableAsync, type AbortableAsyncState } from './use_abortable_async';
import { useKibana } from './use_kibana';
@@ -21,7 +22,7 @@ export function useConversation({
connectorId,
}: {
conversationId?: string;
- chatService?: ObservabilityAIAssistantChatService;
+ chatService?: ObservabilityAIAssistantChatService; // will eventually resolve to a non-nullish value
connectorId: string | undefined;
}): {
conversation: AbortableAsyncState;
@@ -41,6 +42,19 @@ export function useConversation({
const [displayedMessages, setDisplayedMessages] = useState([]);
+ const displayedMessagesWithHardcodedSystemMessage = useMemo(() => {
+ if (!chatService) {
+ return displayedMessages;
+ }
+ const systemMessage = getAssistantSetupMessage({ contexts: chatService?.getContexts() || [] });
+
+ if (displayedMessages[0]?.message.role === MessageRole.User) {
+ return [systemMessage, ...displayedMessages];
+ }
+
+ return [systemMessage, ...displayedMessages.slice(1)];
+ }, [displayedMessages, chatService]);
+
const conversation: AbortableAsyncState =
useAbortableAsync(
({ signal }) => {
@@ -71,7 +85,7 @@ export function useConversation({
return {
conversation,
- displayedMessages,
+ displayedMessages: displayedMessagesWithHardcodedSystemMessage,
setDisplayedMessages,
save: (messages: Message[], handleRefreshConversations?: () => void) => {
const conversationObject = conversation.value!;
diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.test.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.test.ts
index ded7b4b382285..299164e6f52e6 100644
--- a/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.test.ts
+++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.test.ts
@@ -171,6 +171,8 @@ describe('useTimeline', () => {
return subject;
}),
executeFunction: jest.fn(),
+ hasFunction: jest.fn(),
+ hasRenderFunction: jest.fn(),
},
onChatUpdate: jest.fn().mockImplementation((messages) => {
props = { ...props, messages };
diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.ts
index ed6a5a6d2b481..93f1cec5d6c14 100644
--- a/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.ts
+++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.ts
@@ -5,13 +5,13 @@
* 2.0.
*/
+import { i18n } from '@kbn/i18n';
import { AbortError } from '@kbn/kibana-utils-plugin/common';
import type { AuthenticatedUser } from '@kbn/security-plugin/common';
import { last } from 'lodash';
import { useEffect, useMemo, useRef, useState } from 'react';
-import { isObservable, Observable, Subscription } from 'rxjs';
import usePrevious from 'react-use/lib/usePrevious';
-import { i18n } from '@kbn/i18n';
+import { isObservable, Observable, Subscription } from 'rxjs';
import {
ContextDefinition,
MessageRole,
@@ -20,8 +20,8 @@ import {
} from '../../common/types';
import type { ChatPromptEditorProps } from '../components/chat/chat_prompt_editor';
import type { ChatTimelineProps } from '../components/chat/chat_timeline';
+import { ChatActionClickType } from '../components/chat/types';
import { EMPTY_CONVERSATION_TITLE } from '../i18n';
-import { getAssistantSetupMessage } from '../service/get_assistant_setup_message';
import type { ObservabilityAIAssistantChatService, PendingMessage } from '../types';
import {
getTimelineItemsfromConversation,
@@ -29,7 +29,6 @@ import {
} from '../utils/get_timeline_items_from_conversation';
import type { UseGenAIConnectorsResult } from './use_genai_connectors';
import { useKibana } from './use_kibana';
-import { ChatActionClickType } from '../components/chat/types';
export function createNewConversation({
contexts,
@@ -38,7 +37,7 @@ export function createNewConversation({
}): ConversationCreateRequest {
return {
'@timestamp': new Date().toISOString(),
- messages: [getAssistantSetupMessage({ contexts })],
+ messages: [],
conversation: {
title: EMPTY_CONVERSATION_TITLE,
},
@@ -114,55 +113,71 @@ export function useTimeline({
): Promise {
const controller = new AbortController();
- return new Promise((resolve, reject) => {
- if (!connectorId) {
- reject(new Error('Can not add a message without a connector'));
- return;
- }
+ return new Promise(async (resolve, reject) => {
+ try {
+ if (!connectorId) {
+ reject(new Error('Can not add a message without a connector'));
+ return;
+ }
- onChatUpdate(nextMessages);
+ const isStartOfConversation =
+ nextMessages.some((message) => message.message.role === MessageRole.Assistant) === false;
- const lastMessage = last(nextMessages);
+ if (isStartOfConversation && chatService.hasFunction('recall')) {
+ nextMessages = nextMessages.concat({
+ '@timestamp': new Date().toISOString(),
+ message: {
+ role: MessageRole.Assistant,
+ content: '',
+ function_call: {
+ name: 'recall',
+ arguments: JSON.stringify({ queries: [], contexts: [] }),
+ trigger: MessageRole.User,
+ },
+ },
+ });
+ }
- if (lastMessage?.message.function_call?.name) {
- // the user has edited a function suggestion, no need to talk to
- resolve(undefined);
- return;
- }
+ onChatUpdate(nextMessages);
+ const lastMessage = last(nextMessages);
+ if (lastMessage?.message.function_call?.name) {
+ // the user has edited a function suggestion, no need to talk to the LLM
+ resolve(undefined);
+ return;
+ }
- response$ =
- response$ ||
- chatService!.chat({
- messages: nextMessages,
- connectorId,
+ response$ =
+ response$ ||
+ chatService!.chat({
+ messages: nextMessages,
+ connectorId,
+ });
+ let pendingMessageLocal = pendingMessage;
+ const nextSubscription = response$.subscribe({
+ next: (nextPendingMessage) => {
+ pendingMessageLocal = nextPendingMessage;
+ setPendingMessage(() => nextPendingMessage);
+ },
+ error: reject,
+ complete: () => {
+ const error = pendingMessageLocal?.error;
+ if (error) {
+ notifications.toasts.addError(error, {
+ title: i18n.translate('xpack.observabilityAiAssistant.failedToLoadResponse', {
+ defaultMessage: 'Failed to load response from the AI Assistant',
+ }),
+ });
+ }
+ resolve(pendingMessageLocal!);
+ },
});
-
- let pendingMessageLocal = pendingMessage;
-
- const nextSubscription = response$.subscribe({
- next: (nextPendingMessage) => {
- pendingMessageLocal = nextPendingMessage;
- setPendingMessage(() => nextPendingMessage);
- },
- error: reject,
- complete: () => {
- const error = pendingMessageLocal?.error;
-
- if (error) {
- notifications.toasts.addError(error, {
- title: i18n.translate('xpack.observabilityAiAssistant.failedToLoadResponse', {
- defaultMessage: 'Failed to load response from the AI Assistant',
- }),
- });
- }
- resolve(pendingMessageLocal!);
- },
- });
-
- setSubscription(() => {
- controllerRef.current = controller;
- return nextSubscription;
- });
+ setSubscription(() => {
+ controllerRef.current = controller;
+ return nextSubscription;
+ });
+ } catch (error) {
+ reject(error);
+ }
}).then(async (reply) => {
if (reply?.error) {
return nextMessages;
diff --git a/x-pack/plugins/observability_ai_assistant/public/service/create_chat_service.ts b/x-pack/plugins/observability_ai_assistant/public/service/create_chat_service.ts
index 269e81101d8aa..8dc61e6d48449 100644
--- a/x-pack/plugins/observability_ai_assistant/public/service/create_chat_service.ts
+++ b/x-pack/plugins/observability_ai_assistant/public/service/create_chat_service.ts
@@ -145,6 +145,9 @@ export async function createChatService({
},
getContexts,
getFunctions,
+ hasFunction: (name: string) => {
+ return !!getFunctions().find((fn) => fn.options.name === name);
+ },
hasRenderFunction: (name: string) => {
return !!getFunctions().find((fn) => fn.options.name === name)?.render;
},
diff --git a/x-pack/plugins/observability_ai_assistant/public/service/get_assistant_setup_message.ts b/x-pack/plugins/observability_ai_assistant/public/service/get_assistant_setup_message.ts
index 4f52dfb9b9733..c7e865606aaf0 100644
--- a/x-pack/plugins/observability_ai_assistant/public/service/get_assistant_setup_message.ts
+++ b/x-pack/plugins/observability_ai_assistant/public/service/get_assistant_setup_message.ts
@@ -7,9 +7,9 @@
import { without } from 'lodash';
import { MessageRole } from '../../common';
-import { ContextDefinition } from '../../common/types';
+import type { ContextDefinition, Message } from '../../common/types';
-export function getAssistantSetupMessage({ contexts }: { contexts: ContextDefinition[] }) {
+export function getAssistantSetupMessage({ contexts }: { contexts: ContextDefinition[] }): Message {
const coreContext = contexts.find((context) => context.name === 'core')!;
const otherContexts = without(contexts.concat(), coreContext);
diff --git a/x-pack/plugins/observability_ai_assistant/public/types.ts b/x-pack/plugins/observability_ai_assistant/public/types.ts
index 5e985f5ae5f97..99853b7b313b7 100644
--- a/x-pack/plugins/observability_ai_assistant/public/types.ts
+++ b/x-pack/plugins/observability_ai_assistant/public/types.ts
@@ -59,6 +59,7 @@ export interface ObservabilityAIAssistantChatService {
}) => Observable;
getContexts: () => ContextDefinition[];
getFunctions: (options?: { contexts?: string[]; filter?: string }) => FunctionDefinition[];
+ hasFunction: (name: string) => boolean;
hasRenderFunction: (name: string) => boolean;
executeFunction: ({}: {
name: string;
diff --git a/x-pack/plugins/observability_ai_assistant/public/utils/storybook_decorator.tsx b/x-pack/plugins/observability_ai_assistant/public/utils/storybook_decorator.tsx
index 3c4dc569ab5e5..68dd784a1f8aa 100644
--- a/x-pack/plugins/observability_ai_assistant/public/utils/storybook_decorator.tsx
+++ b/x-pack/plugins/observability_ai_assistant/public/utils/storybook_decorator.tsx
@@ -34,6 +34,7 @@ const chatService: ObservabilityAIAssistantChatService = {
renderFunction: (name: string, args: string | undefined, response: {}) => (
Hello! {name}
),
+ hasFunction: () => true,
hasRenderFunction: () => true,
};
diff --git a/x-pack/plugins/observability_ai_assistant/server/routes/chat/route.ts b/x-pack/plugins/observability_ai_assistant/server/routes/chat/route.ts
index a250a9e8e0915..19ebdcbaedc95 100644
--- a/x-pack/plugins/observability_ai_assistant/server/routes/chat/route.ts
+++ b/x-pack/plugins/observability_ai_assistant/server/routes/chat/route.ts
@@ -9,7 +9,6 @@ import { IncomingMessage } from 'http';
import * as t from 'io-ts';
import { toBooleanRt } from '@kbn/io-ts-utils';
import type { CreateChatCompletionResponse } from 'openai';
-import { MessageRole } from '../../../common';
import { createObservabilityAIAssistantServerRoute } from '../create_observability_ai_assistant_server_route';
import { messageRt } from '../runtime_types';
@@ -49,25 +48,12 @@ const chatRoute = createObservabilityAIAssistantServerRoute({
}
const {
- body: { messages, connectorId, functions, functionCall: givenFunctionCall },
+ body: { messages, connectorId, functions, functionCall },
query = { stream: true },
} = params;
const stream = query.stream;
- let functionCall = givenFunctionCall;
-
- if (!functionCall) {
- const isStartOfConversation =
- messages.some((message) => message.message.role === MessageRole.Assistant) === false;
-
- const isRecallFunctionAvailable = functions.some((fn) => fn.name === 'recall') === true;
-
- const willUseRecall = isStartOfConversation && isRecallFunctionAvailable;
-
- functionCall = willUseRecall ? 'recall' : undefined;
- }
-
return client.chat({
messages,
connectorId,
diff --git a/x-pack/plugins/profiling/public/components/frame_information_window/frame_information_ai_assistant.tsx b/x-pack/plugins/profiling/public/components/frame_information_window/frame_information_ai_assistant.tsx
index 01f27ef54e72f..9175115432f81 100644
--- a/x-pack/plugins/profiling/public/components/frame_information_window/frame_information_ai_assistant.tsx
+++ b/x-pack/plugins/profiling/public/components/frame_information_window/frame_information_ai_assistant.tsx
@@ -30,14 +30,6 @@ export function FrameInformationAIAssistant({ frame }: Props) {
const now = new Date().toISOString();
return [
- {
- '@timestamp': now,
- message: {
- role: MessageRole.System,
- content: `You are perf-gpt, a helpful assistant for performance analysis and optimisation
- of software. Answer as concisely as possible.`,
- },
- },
{
'@timestamp': now,
message: {
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx
index 0f6a3fad1eb66..71c41b5dd5c9f 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx
@@ -88,8 +88,6 @@ const GraphTab = tabWithSuspense(lazy(() => import('../graph_tab_content')));
const NotesTab = tabWithSuspense(lazy(() => import('../notes_tab_content')));
const PinnedTab = tabWithSuspense(lazy(() => import('../pinned_tab_content')));
const SessionTab = tabWithSuspense(lazy(() => import('../session_tab_content')));
-const DiscoverTab = tabWithSuspense(lazy(() => import('../discover_tab_content')));
-
interface BasicTimelineTab {
renderCellValue: (props: CellValueElementProps) => React.ReactNode;
rowRenderers: RowRenderer[];
@@ -134,7 +132,6 @@ const ActiveTimelineTab = memo(
setConversationId,
showTimeline,
}) => {
- const isDiscoverInTimelineEnabled = useIsExperimentalFeatureEnabled('discoverInTimeline');
const { hasAssistantPrivilege } = useAssistantAvailability();
const getTab = useCallback(
(tab: TimelineTabs) => {
@@ -231,14 +228,6 @@ const ActiveTimelineTab = memo(
)}
)}
- {isDiscoverInTimelineEnabled && (
-
-
-
- )}
>
);
}
diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/links/constants.ts b/x-pack/plugins/security_solution_serverless/public/navigation/links/constants.ts
index 565b2d2c97c80..dca89a4a00111 100644
--- a/x-pack/plugins/security_solution_serverless/public/navigation/links/constants.ts
+++ b/x-pack/plugins/security_solution_serverless/public/navigation/links/constants.ts
@@ -30,6 +30,8 @@ export const SecurityPagePath = {
* The path should not be used for links displayed in the main left navigation, since highlighting won't work.
**/
export enum ExternalPageName {
+ // Discover
+ discover = 'discover:',
// Osquery
osquery = 'osquery:',
// Analytics
diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/links/nav.links.test.ts b/x-pack/plugins/security_solution_serverless/public/navigation/links/nav.links.test.ts
index 0a20899462c20..d06b28df01955 100644
--- a/x-pack/plugins/security_solution_serverless/public/navigation/links/nav.links.test.ts
+++ b/x-pack/plugins/security_solution_serverless/public/navigation/links/nav.links.test.ts
@@ -57,6 +57,11 @@ const projectLinkDevTools: ProjectNavigationLink = {
title: 'Dev tools',
};
+const projectLinkDiscover: ProjectNavigationLink = {
+ id: ExternalPageName.discover,
+ title: 'Discover',
+};
+
const chromeNavLink1: ChromeNavLink = {
id: `${APP_UI_ID}:${link1.id}`,
title: link1.title,
@@ -145,6 +150,7 @@ describe('getProjectNavLinks', () => {
link1,
link2,
{ ...linkMlLanding, categories: mlNavCategories, links: mlNavLinks },
+ projectLinkDiscover,
projectLinkDevTools,
]);
});
@@ -169,6 +175,7 @@ describe('getProjectNavLinks', () => {
expect(value).toEqual([
link1,
{ ...linkAssets, links: [...assetsNavLinks, link2] },
+ projectLinkDiscover,
projectLinkDevTools,
]);
});
@@ -193,6 +200,7 @@ describe('getProjectNavLinks', () => {
expect(value).toEqual([
link1,
{ ...linkInvestigations, links: [link2, ...investigationsNavLinks] },
+ projectLinkDiscover,
projectLinkDevTools,
]);
});
@@ -226,6 +234,7 @@ describe('getProjectNavLinks', () => {
categories: projectSettingsNavCategories,
links: [...expectedProjectSettingsNavLinks, link2],
},
+ projectLinkDiscover,
projectLinkDevTools,
]);
});
diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/links/nav_links.ts b/x-pack/plugins/security_solution_serverless/public/navigation/links/nav_links.ts
index 2da3279562191..76f4e752af6c5 100644
--- a/x-pack/plugins/security_solution_serverless/public/navigation/links/nav_links.ts
+++ b/x-pack/plugins/security_solution_serverless/public/navigation/links/nav_links.ts
@@ -18,6 +18,7 @@ import {
projectSettingsNavLinks,
} from './sections/project_settings_links';
import { devToolsNavLink } from './sections/dev_tools_links';
+import { discoverNavLink } from './sections/discover_links';
import type { ProjectNavigationLink } from './types';
import { getCloudLinkKey, getCloudUrl, getNavLinkIdFromProjectPageName, isCloudLink } from './util';
import { investigationsNavLinks } from './sections/investigations_links';
@@ -55,6 +56,9 @@ const processNavLinks = (
): ProjectNavigationLink[] => {
const projectNavLinks: ProjectNavigationLink[] = [...securityNavLinks];
+ // Discover. just pushing it
+ projectNavLinks.push(discoverNavLink);
+
// Investigations. injecting external sub-links and categories definition to the landing
const investigationsLinkIndex = projectNavLinks.findIndex(
({ id }) => id === SecurityPageName.investigations
diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/links/sections/discover_links.ts b/x-pack/plugins/security_solution_serverless/public/navigation/links/sections/discover_links.ts
new file mode 100644
index 0000000000000..8c0bf26ec2d12
--- /dev/null
+++ b/x-pack/plugins/security_solution_serverless/public/navigation/links/sections/discover_links.ts
@@ -0,0 +1,15 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { ExternalPageName } from '../constants';
+import type { ProjectNavigationLink } from '../types';
+import { DISCOVER_TITLE } from './discover_translations';
+
+export const discoverNavLink: ProjectNavigationLink = {
+ id: ExternalPageName.discover,
+ title: DISCOVER_TITLE,
+};
diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/links/sections/discover_translations.ts b/x-pack/plugins/security_solution_serverless/public/navigation/links/sections/discover_translations.ts
new file mode 100644
index 0000000000000..0c1212c187aa2
--- /dev/null
+++ b/x-pack/plugins/security_solution_serverless/public/navigation/links/sections/discover_translations.ts
@@ -0,0 +1,15 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const DISCOVER_TITLE = i18n.translate(
+ 'xpack.securitySolutionServerless.navLinks.discover.title',
+ {
+ defaultMessage: 'Discover',
+ }
+);
diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/categories.ts b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/categories.ts
index ebefe9b77a70b..e7c9057d950ff 100644
--- a/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/categories.ts
+++ b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/categories.ts
@@ -15,7 +15,7 @@ import { ExternalPageName } from '../links/constants';
export const CATEGORIES: SeparatorLinkCategory[] = [
{
type: LinkCategoryType.separator,
- linkIds: [SecurityPageName.dashboards],
+ linkIds: [ExternalPageName.discover, SecurityPageName.dashboards],
},
{
type: LinkCategoryType.separator,
diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json
index f3514ba7fbb65..c1f3af869cb78 100644
--- a/x-pack/plugins/translations/translations/fr-FR.json
+++ b/x-pack/plugins/translations/translations/fr-FR.json
@@ -229,6 +229,8 @@
"coloring.dynamicColoring.rangeType.label": "Type de valeur",
"coloring.dynamicColoring.rangeType.number": "Numéro",
"coloring.dynamicColoring.rangeType.percent": "Pourcent",
+ "coloring.colorMapping.terms.otherBucketLabel": "Autre",
+ "coloring.colorMapping.terms.emptyLabel": "(vide)",
"console.helpPage.learnAboutConsoleAndQueryDslText": "En savoir plus sur {console} et {queryDsl}",
"console.historyPage.itemOfRequestListAriaLabel": "Requête : {historyItem}",
"console.settingsPage.refreshInterval.everyNMinutesTimeInterval": "Toutes les {value} {value, plural, one {minute} many {minutes} other {minutes}}",
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index d078f535b6c99..18492c43f4216 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -229,6 +229,8 @@
"coloring.dynamicColoring.rangeType.label": "値型",
"coloring.dynamicColoring.rangeType.number": "数字",
"coloring.dynamicColoring.rangeType.percent": "割合(%)",
+ "coloring.colorMapping.terms.otherBucketLabel":"その他",
+ "coloring.colorMapping.terms.emptyLabel": "(空)",
"console.helpPage.learnAboutConsoleAndQueryDslText": "{console}と{queryDsl}についてさらに詳しく",
"console.historyPage.itemOfRequestListAriaLabel": "リクエスト:{historyItem}",
"console.settingsPage.refreshInterval.everyNMinutesTimeInterval": "{value}{value, plural, other {分}}毎",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 3b8006085b1da..296b107b99e5c 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -229,6 +229,8 @@
"coloring.dynamicColoring.rangeType.label": "值类型",
"coloring.dynamicColoring.rangeType.number": "数字",
"coloring.dynamicColoring.rangeType.percent": "百分比",
+ "coloring.colorMapping.terms.otherBucketLabel":"其他",
+ "coloring.colorMapping.terms.emptyLabel": "(空)",
"console.helpPage.learnAboutConsoleAndQueryDslText": "了解 {console} 和 {queryDsl}",
"console.historyPage.itemOfRequestListAriaLabel": "请求:{historyItem}",
"console.settingsPage.refreshInterval.everyNMinutesTimeInterval": "每 {value} {value, plural, other {分钟}}",
diff --git a/x-pack/test/api_integration/apis/ml/modules/get_module.ts b/x-pack/test/api_integration/apis/ml/modules/get_module.ts
index 488714f13c399..7c72f18f918e3 100644
--- a/x-pack/test/api_integration/apis/ml/modules/get_module.ts
+++ b/x-pack/test/api_integration/apis/ml/modules/get_module.ts
@@ -49,8 +49,7 @@ export default ({ getService }: FtrProviderContext) => {
return body;
}
- // FLAKY: https://github.com/elastic/kibana/issues/164420
- describe.skip('get_module', function () {
+ describe('get_module', function () {
before(async () => {
await ml.testResources.setKibanaTimeZoneToUTC();
});
diff --git a/x-pack/test/api_integration/apis/ml/modules/index.ts b/x-pack/test/api_integration/apis/ml/modules/index.ts
index d28263b487701..68db43fd0ea3c 100644
--- a/x-pack/test/api_integration/apis/ml/modules/index.ts
+++ b/x-pack/test/api_integration/apis/ml/modules/index.ts
@@ -26,6 +26,10 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
const version = await ml.testResources.installFleetPackage(fleetPackage);
installedPackages.push({ pkgName: fleetPackage, version });
}
+
+ // ensure fleet installed packages are ready
+ await ml.testResources.assertModuleExists('apache_data_stream');
+ await ml.testResources.assertModuleExists('nginx_data_stream');
});
after(async () => {
diff --git a/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts
index e3b0d06fbc5a1..e9519a0122203 100644
--- a/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts
+++ b/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts
@@ -32,6 +32,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
const registry = getService('registry');
const supertest = getService('supertest');
const es = getService('es');
+ const logger = getService('log');
const apmApiClient = getService('apmApiClient');
const synthtraceEsClient = getService('synthtraceEsClient');
@@ -133,9 +134,13 @@ export default function ApiTest({ getService }: FtrProviderContext) {
});
after(async () => {
- await deleteActionConnector({ supertest, es, actionId });
- await deleteRuleById({ supertest, ruleId });
- await deleteAlertsByRuleId({ es, ruleId });
+ try {
+ await deleteActionConnector({ supertest, es, actionId });
+ await deleteRuleById({ supertest, ruleId });
+ await deleteAlertsByRuleId({ es, ruleId });
+ } catch (e) {
+ logger.info('Could not delete rule or action connector', e);
+ }
});
it('checks if rule is active', async () => {
diff --git a/x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts
index 3dbfa93a69d46..cf4b713aa9586 100644
--- a/x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts
+++ b/x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts
@@ -32,6 +32,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
const registry = getService('registry');
const supertest = getService('supertest');
const es = getService('es');
+ const logger = getService('log');
const apmApiClient = getService('apmApiClient');
const synthtraceEsClient = getService('synthtraceEsClient');
@@ -106,9 +107,13 @@ export default function ApiTest({ getService }: FtrProviderContext) {
});
after(async () => {
- await deleteActionConnector({ supertest, es, actionId });
- await deleteAlertsByRuleId({ es, ruleId });
- await deleteRuleById({ supertest, ruleId });
+ try {
+ await deleteActionConnector({ supertest, es, actionId });
+ await deleteRuleById({ supertest, ruleId });
+ await deleteAlertsByRuleId({ es, ruleId });
+ } catch (e) {
+ logger.info('Could not delete rule or action connector', e);
+ }
});
it('checks if rule is active', async () => {
diff --git a/x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts
index e51372f3d8b01..394db386c80e7 100644
--- a/x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts
+++ b/x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts
@@ -32,6 +32,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
const registry = getService('registry');
const supertest = getService('supertest');
const es = getService('es');
+ const logger = getService('log');
const apmApiClient = getService('apmApiClient');
const synthtraceEsClient = getService('synthtraceEsClient');
@@ -116,9 +117,13 @@ export default function ApiTest({ getService }: FtrProviderContext) {
});
after(async () => {
- await deleteActionConnector({ supertest, es, actionId });
- await deleteRuleById({ supertest, ruleId });
- await deleteAlertsByRuleId({ es, ruleId });
+ try {
+ await deleteActionConnector({ supertest, es, actionId });
+ await deleteRuleById({ supertest, ruleId });
+ await deleteAlertsByRuleId({ es, ruleId });
+ } catch (e) {
+ logger.info('Could not delete rule or action connector', e);
+ }
});
it('checks if rule is active', async () => {
diff --git a/x-pack/test/apm_api_integration/tests/index.ts b/x-pack/test/apm_api_integration/tests/index.ts
index 8f4be6ad1652c..7b4ab43aaaeae 100644
--- a/x-pack/test/apm_api_integration/tests/index.ts
+++ b/x-pack/test/apm_api_integration/tests/index.ts
@@ -26,10 +26,7 @@ function getGlobPattern() {
export default function apmApiIntegrationTests({ getService, loadTestFile }: FtrProviderContext) {
const registry = getService('registry');
- // Failing: See https://github.com/elastic/kibana/issues/167973
- // FLAKY: https://github.com/elastic/kibana/issues/167974
- // Failing: See https://github.com/elastic/kibana/issues/167975
- describe.skip('APM API tests', function () {
+ describe('APM API tests', function () {
const filePattern = getGlobPattern();
const tests = globby.sync(filePattern, { cwd });
diff --git a/x-pack/test/functional/services/ml/api.ts b/x-pack/test/functional/services/ml/api.ts
index cf71764ee5397..b514d18d552ad 100644
--- a/x-pack/test/functional/services/ml/api.ts
+++ b/x-pack/test/functional/services/ml/api.ts
@@ -1552,5 +1552,14 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) {
log.debug('Module set up');
return module;
},
+
+ async getModule(moduleId: string) {
+ log.debug(`Get module with ID: "${moduleId}"`);
+ const { body: module, status } = await kbnSupertest
+ .get(`/internal/ml/modules/get_module/${moduleId}`)
+ .set(getCommonRequestHeader('1'));
+ this.assertResponseStatusCode(200, status, module);
+ return module;
+ },
};
}
diff --git a/x-pack/test/functional/services/ml/test_resources.ts b/x-pack/test/functional/services/ml/test_resources.ts
index 8771db60eabb6..22f43a08f1e78 100644
--- a/x-pack/test/functional/services/ml/test_resources.ts
+++ b/x-pack/test/functional/services/ml/test_resources.ts
@@ -641,5 +641,11 @@ export function MachineLearningTestResourcesProvider(
async clearAdvancedSettingProperty(propertyName: string) {
await kibanaServer.uiSettings.unset(propertyName);
},
+
+ async assertModuleExists(moduleId: string) {
+ await retry.tryForTime(30 * 1000, async () => {
+ await mlApi.getModule(moduleId);
+ });
+ },
};
}
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_state.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_state.cy.ts
index 7d438791c3f1b..06c934083cd88 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_state.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_state.cy.ts
@@ -33,7 +33,8 @@ const INITIAL_END_DATE = 'Jan 19, 2024 @ 20:33:29.186';
const DEFAULT_ESQL_QUERY =
'from .alerts-security.alerts-default,apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-* | limit 10';
-describe(
+// TODO: reuse or remove this tests when ESQL tab will be added
+describe.skip(
'Discover State',
{
env: { ftrConfig: { enableExperimental: ['discoverInTimeline'] } },
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_timeline_state_integration.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_timeline_state_integration.cy.ts
index 6e1289f9f8450..71b268e090b56 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_timeline_state_integration.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/discover_timeline_state_integration.cy.ts
@@ -60,7 +60,8 @@ const TIMELINE_RESPONSE_SAVED_OBJECT_ID_PATH =
'response.body.data.persistTimeline.timeline.savedObjectId';
const esqlQuery = 'from auditbeat-* | where ecs.version == "8.0.0"';
-describe(
+// TODO: reuse or remove this tests when ESQL tab will be added
+describe.skip(
'Discover Timeline State Integration',
{
env: { ftrConfig: { enableExperimental: ['discoverInTimeline'] } },
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/search_filter.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/search_filter.cy.ts
index 42375aeee5e2e..cfe3ca421de36 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/search_filter.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/discover/search_filter.cy.ts
@@ -34,7 +34,8 @@ const NEW_START_DATE = 'Jan 18, 2023 @ 20:33:29.186';
const esqlQuery = 'from auditbeat-* | where ecs.version == "8.0.0"';
// Failing: See https://github.com/elastic/kibana/issues/167186
-describe(
+// TODO: reuse or remove this tests when ESQL tab will be added
+describe.skip(
'Basic discover search and filter operations',
{
env: { ftrConfig: { enableExperimental: ['discoverInTimeline'] } },
diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts
index cfb133c17caf4..441dc06cab829 100644
--- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts
+++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/rules.ts
@@ -35,8 +35,7 @@ export default function ({ getService }: FtrProviderContext) {
const esClient = getService('es');
const esDeleteAllIndices = getService('esDeleteAllIndices');
- // Failing: See https://github.com/elastic/kibana/issues/167665
- describe.skip('Alerting rules', () => {
+ describe('Alerting rules', () => {
const RULE_TYPE_ID = '.es-query';
const ALERT_ACTION_INDEX = 'alert-action-es-query';
let actionId: string;
@@ -53,6 +52,7 @@ export default function ({ getService }: FtrProviderContext) {
.set('x-elastic-internal-origin', 'foo');
await esClient.deleteByQuery({
index: '.kibana-event-log-*',
+ conflicts: 'proceed',
query: { term: { 'kibana.alert.rule.consumer': 'alerts' } },
});
await esDeleteAllIndices([ALERT_ACTION_INDEX]);
diff --git a/x-pack/test_serverless/functional/services/index.ts b/x-pack/test_serverless/functional/services/index.ts
index da2688b22b645..125c93de2fcff 100644
--- a/x-pack/test_serverless/functional/services/index.ts
+++ b/x-pack/test_serverless/functional/services/index.ts
@@ -13,6 +13,7 @@ import { SvlObltNavigationServiceProvider } from './svl_oblt_navigation';
import { SvlSearchNavigationServiceProvider } from './svl_search_navigation';
import { SvlSecNavigationServiceProvider } from './svl_sec_navigation';
import { SvlCommonScreenshotsProvider } from './svl_common_screenshots';
+import { SvlCasesServiceProvider } from '../../api_integration/services/svl_cases';
import { MachineLearningProvider } from './ml';
export const services = {
@@ -26,5 +27,6 @@ export const services = {
svlSearchNavigation: SvlSearchNavigationServiceProvider,
svlSecNavigation: SvlSecNavigationServiceProvider,
svlCommonScreenshots: SvlCommonScreenshotsProvider,
+ svlCases: SvlCasesServiceProvider,
svlMl: MachineLearningProvider,
};
diff --git a/x-pack/test_serverless/functional/test_suites/common/management/index_management/create_enrich_policy.ts b/x-pack/test_serverless/functional/test_suites/common/management/index_management/create_enrich_policy.ts
index f5b51c815e0fe..b3e248ea82de4 100644
--- a/x-pack/test_serverless/functional/test_suites/common/management/index_management/create_enrich_policy.ts
+++ b/x-pack/test_serverless/functional/test_suites/common/management/index_management/create_enrich_policy.ts
@@ -20,6 +20,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
const POLICY_NAME = `policy-${Math.random()}`;
describe('Create enrich policy', function () {
+ // TimeoutError: Waiting for element to be located By(css selector, [data-test-subj="enrichPoliciesEmptyPromptCreateButton"])
+ this.tags(['failsOnMKI']);
before(async () => {
log.debug('Creating test index');
try {
diff --git a/x-pack/test_serverless/functional/test_suites/observability/cases/attachment_framework.ts b/x-pack/test_serverless/functional/test_suites/observability/cases/attachment_framework.ts
index bfd2b8700d785..cafe2d867241d 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/cases/attachment_framework.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/cases/attachment_framework.ts
@@ -20,7 +20,9 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const cases = getService('cases');
const find = getService('find');
- describe('Cases persistable attachments', () => {
+ describe('Cases persistable attachments', function () {
+ // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
+ this.tags(['failsOnMKI']);
describe('lens visualization', () => {
before(async () => {
await svlCommonPage.login();
diff --git a/x-pack/test_serverless/functional/test_suites/observability/cases/create_case_form.ts b/x-pack/test_serverless/functional/test_suites/observability/cases/create_case_form.ts
index e89e2b799dc30..e0166ac9de2df 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/cases/create_case_form.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/cases/create_case_form.ts
@@ -16,6 +16,8 @@ const owner = OBSERVABILITY_OWNER;
export default ({ getService, getPageObject }: FtrProviderContext) => {
describe('Create Case', function () {
+ // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
+ this.tags(['failsOnMKI']);
const find = getService('find');
const cases = getService('cases');
const testSubjects = getService('testSubjects');
diff --git a/x-pack/test_serverless/functional/test_suites/observability/cases/list_view.ts b/x-pack/test_serverless/functional/test_suites/observability/cases/list_view.ts
index 61cbd55f04b66..30fd021e5c6f5 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/cases/list_view.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/cases/list_view.ts
@@ -18,7 +18,9 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const svlCommonPage = getPageObject('svlCommonPage');
const svlObltNavigation = getService('svlObltNavigation');
- describe('Cases list', () => {
+ describe('Cases list', function () {
+ // multiple errors in after hook due to delete permission
+ this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
await svlObltNavigation.navigateToLandingPage();
diff --git a/x-pack/test_serverless/functional/test_suites/observability/cases/view_case.ts b/x-pack/test_serverless/functional/test_suites/observability/cases/view_case.ts
index 24234d4a40e52..ac0fb6fec9d5f 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/cases/view_case.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/cases/view_case.ts
@@ -29,7 +29,9 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const svlCommonNavigation = getPageObject('svlCommonNavigation');
const svlCommonPage = getPageObject('svlCommonPage');
- describe('Case View', () => {
+ describe('Case View', function () {
+ // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
+ this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
});
diff --git a/x-pack/test_serverless/functional/test_suites/observability/config.screenshots.ts b/x-pack/test_serverless/functional/test_suites/observability/config.screenshots.ts
new file mode 100644
index 0000000000000..f9a06826cb755
--- /dev/null
+++ b/x-pack/test_serverless/functional/test_suites/observability/config.screenshots.ts
@@ -0,0 +1,21 @@
+/*
+ * 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 { createTestConfig } from '../../config.base';
+
+const enabledActionTypes = ['.index', '.server-log'];
+
+export default createTestConfig({
+ serverlessProject: 'oblt',
+ testFiles: [require.resolve('./screenshot_creation')],
+ kbnServerArgs: [`--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`],
+ junit: {
+ reportName: 'Serverless Observability Screenshot Creation',
+ },
+
+ esServerArgs: ['xpack.ml.ad.enabled=false', 'xpack.ml.dfa.enabled=false'],
+});
diff --git a/x-pack/test_serverless/functional/test_suites/observability/infra/index.ts b/x-pack/test_serverless/functional/test_suites/observability/infra/index.ts
index c7096467f71bc..80eb2f7a32a53 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/infra/index.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/infra/index.ts
@@ -9,6 +9,8 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('Observability Infra', function () {
+ // TimeoutError: Waiting for element to be located By(css selector, [data-test-subj="infrastructure-alerts-and-rules"])
+ this.tags(['failsOnMKI']);
loadTestFile(require.resolve('./header_menu'));
});
}
diff --git a/x-pack/test_serverless/functional/test_suites/observability/ml/anomaly_detection_jobs_list.ts b/x-pack/test_serverless/functional/test_suites/observability/ml/anomaly_detection_jobs_list.ts
index 10f203889e1ea..05382e03f78d0 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/ml/anomaly_detection_jobs_list.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/ml/anomaly_detection_jobs_list.ts
@@ -13,7 +13,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['svlCommonPage']);
const adJobId = 'fq_single_permission';
- describe('Anomaly detection jobs list', () => {
+ describe('Anomaly detection jobs list', function () {
+ // Error: Failed to delete all indices with pattern [.ml-*]
+ this.tags(['failsOnMKI']);
before(async () => {
await PageObjects.svlCommonPage.login();
diff --git a/x-pack/test_serverless/functional/test_suites/observability/ml/index.ts b/x-pack/test_serverless/functional/test_suites/observability/ml/index.ts
index e50d10b5ce6fd..ba88fce593abf 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/ml/index.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/ml/index.ts
@@ -9,6 +9,9 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('Observability ML', function () {
+ // Error: Failed to delete all indices with pattern [.ml-*]
+ // Error: First result should be Machine Learning (got matching items 'undefined')
+ this.tags(['failsOnMKI']);
loadTestFile(require.resolve('./anomaly_detection_jobs_list'));
loadTestFile(require.resolve('./search_bar_features'));
});
diff --git a/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/index.ts b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/index.ts
new file mode 100644
index 0000000000000..8710906ab6408
--- /dev/null
+++ b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/index.ts
@@ -0,0 +1,14 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { FtrProviderContext } from '../../../ftr_provider_context';
+
+export default function ({ loadTestFile }: FtrProviderContext) {
+ describe('Screenshots - serverless observability UI', function () {
+ loadTestFile(require.resolve('./response_ops_docs'));
+ });
+}
diff --git a/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/cases/index.ts b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/cases/index.ts
new file mode 100644
index 0000000000000..171215dfbf4d3
--- /dev/null
+++ b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/cases/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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { FtrProviderContext } from '../../../../../ftr_provider_context';
+
+export default function ({ loadTestFile, getService }: FtrProviderContext) {
+ const browser = getService('browser');
+
+ describe('observability cases', function () {
+ before(async () => {
+ await browser.setWindowSize(1920, 1080);
+ });
+
+ loadTestFile(require.resolve('./list_view'));
+ });
+}
diff --git a/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/cases/list_view.ts b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/cases/list_view.ts
new file mode 100644
index 0000000000000..cc1d8b5f78995
--- /dev/null
+++ b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/cases/list_view.ts
@@ -0,0 +1,63 @@
+/*
+ * 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 { OBSERVABILITY_OWNER } from '@kbn/cases-plugin/common';
+import { FtrProviderContext } from '../../../../../ftr_provider_context';
+import { navigateToCasesApp } from '../../../../../../shared/lib/cases';
+
+export default function ({ getPageObject, getPageObjects, getService }: FtrProviderContext) {
+ const pageObjects = getPageObjects(['common', 'header', 'svlCommonPage']);
+ const svlCases = getService('svlCases');
+ const svlCommonScreenshots = getService('svlCommonScreenshots');
+ const screenshotDirectories = ['response_ops_docs', 'observability_cases'];
+ const owner = OBSERVABILITY_OWNER;
+
+ describe('list view', function () {
+ before(async () => {
+ await svlCases.api.createCase(
+ svlCases.api.getPostCaseRequest(owner, {
+ title: 'Metrics inventory',
+ tags: ['IBM resilient'],
+ description: 'Test.',
+ owner,
+ })
+ );
+
+ await svlCases.api.createCase(
+ svlCases.api.getPostCaseRequest(owner, {
+ title: 'Logs threshold',
+ tags: ['jira'],
+ description: 'Test.',
+ owner,
+ })
+ );
+
+ await svlCases.api.createCase(
+ svlCases.api.getPostCaseRequest(owner, {
+ title: 'Monitor uptime',
+ tags: ['swimlane'],
+ description: 'Test.',
+ owner,
+ })
+ );
+ });
+
+ after(async () => {
+ await svlCases.api.deleteAllCaseItems();
+ await pageObjects.svlCommonPage.forceLogout();
+ });
+
+ beforeEach(async () => {
+ await pageObjects.svlCommonPage.login();
+ });
+
+ it('cases list screenshot', async () => {
+ await navigateToCasesApp(getPageObject, getService, owner);
+ await svlCommonScreenshots.takeScreenshot('cases', screenshotDirectories, 1700, 1024);
+ });
+ });
+}
diff --git a/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/connectors/index.ts b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/connectors/index.ts
new file mode 100644
index 0000000000000..4e2f00e35e94d
--- /dev/null
+++ b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/connectors/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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { FtrProviderContext } from '../../../../../ftr_provider_context';
+
+export default function ({ loadTestFile, getService }: FtrProviderContext) {
+ const browser = getService('browser');
+
+ describe('observability connectors', function () {
+ before(async () => {
+ await browser.setWindowSize(1920, 1080);
+ });
+
+ loadTestFile(require.resolve('./server_log_connector'));
+ });
+}
diff --git a/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/connectors/server_log_connector.ts b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/connectors/server_log_connector.ts
new file mode 100644
index 0000000000000..7eed7d5cbbd26
--- /dev/null
+++ b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/connectors/server_log_connector.ts
@@ -0,0 +1,39 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { FtrProviderContext } from '../../../../../ftr_provider_context';
+
+export default function ({ getService, getPageObjects }: FtrProviderContext) {
+ const svlCommonScreenshots = getService('svlCommonScreenshots');
+ const screenshotDirectories = ['response_ops_docs', 'observability_connectors'];
+ const pageObjects = getPageObjects(['common', 'header', 'svlCommonPage']);
+ const testSubjects = getService('testSubjects');
+
+ describe('server log connector', function () {
+ beforeEach(async () => {
+ await pageObjects.svlCommonPage.login();
+ });
+
+ after(async () => {
+ await pageObjects.svlCommonPage.forceLogout();
+ });
+
+ it('server log connector screenshots', async () => {
+ await pageObjects.common.navigateToApp('connectors');
+ await pageObjects.header.waitUntilLoadingHasFinished();
+ await testSubjects.click('createFirstActionButton');
+ await testSubjects.click(`.server-log-card`);
+ await testSubjects.setValue('nameInput', 'Server log test connector');
+ await svlCommonScreenshots.takeScreenshot('serverlog-connector', screenshotDirectories);
+ const saveTestButton = await testSubjects.find('create-connector-flyout-save-test-btn');
+ await saveTestButton.click();
+ await svlCommonScreenshots.takeScreenshot('serverlog-params-test', screenshotDirectories);
+ const flyOutCancelButton = await testSubjects.find('euiFlyoutCloseButton');
+ await flyOutCancelButton.click();
+ });
+ });
+}
diff --git a/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/index.ts b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/index.ts
new file mode 100644
index 0000000000000..10b33b5fc944d
--- /dev/null
+++ b/x-pack/test_serverless/functional/test_suites/observability/screenshot_creation/response_ops_docs/index.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { FtrProviderContext } from '../../../../ftr_provider_context';
+
+export default function ({ getService, loadTestFile }: FtrProviderContext) {
+ const browser = getService('browser');
+ const ml = getService('ml');
+
+ describe('response ops docs', function () {
+ this.tags(['responseOps']);
+
+ before(async () => {
+ await ml.testResources.setKibanaTimeZoneToUTC();
+ await ml.testResources.disableKibanaAnnouncements();
+ await browser.setWindowSize(1920, 1080);
+ });
+
+ after(async () => {
+ await ml.testResources.resetKibanaTimeZone();
+ await ml.testResources.resetKibanaAnnouncements();
+ });
+
+ loadTestFile(require.resolve('./cases'));
+ loadTestFile(require.resolve('./connectors'));
+ });
+}
diff --git a/x-pack/test_serverless/functional/test_suites/search/config.screenshots.ts b/x-pack/test_serverless/functional/test_suites/search/config.screenshots.ts
index b0c951ef3295c..3a8966904b5c9 100644
--- a/x-pack/test_serverless/functional/test_suites/search/config.screenshots.ts
+++ b/x-pack/test_serverless/functional/test_suites/search/config.screenshots.ts
@@ -7,7 +7,7 @@
import { createTestConfig } from '../../config.base';
-const enabledActionTypes = ['.index', '.server-log'];
+const enabledActionTypes = ['.index'];
export default createTestConfig({
serverlessProject: 'es',
diff --git a/x-pack/test_serverless/functional/test_suites/search/empty_page.ts b/x-pack/test_serverless/functional/test_suites/search/empty_page.ts
index e3e82c0b0d97b..0f3be7d382f90 100644
--- a/x-pack/test_serverless/functional/test_suites/search/empty_page.ts
+++ b/x-pack/test_serverless/functional/test_suites/search/empty_page.ts
@@ -14,6 +14,8 @@ export default function ({ getPageObject, getService }: FtrProviderContext) {
const svlCommonPage = getPageObject('svlCommonPage');
describe('empty pages', function () {
+ // Error: expected testSubject(kbnOverviewElasticsearchGettingStarted) to exist
+ this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
await svlSearchNavigation.navigateToLandingPage();
diff --git a/x-pack/test_serverless/functional/test_suites/search/screenshot_creation/response_ops_docs/stack_connectors/index.ts b/x-pack/test_serverless/functional/test_suites/search/screenshot_creation/response_ops_docs/stack_connectors/index.ts
index f5f06e36d3ca7..e5d251e691183 100644
--- a/x-pack/test_serverless/functional/test_suites/search/screenshot_creation/response_ops_docs/stack_connectors/index.ts
+++ b/x-pack/test_serverless/functional/test_suites/search/screenshot_creation/response_ops_docs/stack_connectors/index.ts
@@ -18,14 +18,6 @@ export default function ({ loadTestFile, getService }: FtrProviderContext) {
describe('stack connectors', function () {
before(async () => {
await browser.setWindowSize(1920, 1080);
- await actions.api.createConnector({
- name: 'server-log-connector',
- config: {},
- secrets: {},
- connectorTypeId: '.server-log',
- additionalRequestHeaders: svlCommonApi.getInternalRequestHeader(),
- });
-
await es.indices.create({
index: testIndex,
body: {
@@ -58,6 +50,5 @@ export default function ({ loadTestFile, getService }: FtrProviderContext) {
});
loadTestFile(require.resolve('./connectors'));
- // loadTestFile(require.resolve('./connector_types'));
});
}
diff --git a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/configure.ts b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/configure.ts
index ca9e9ab6219e7..6eb306d43ad00 100644
--- a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/configure.ts
+++ b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/configure.ts
@@ -18,6 +18,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const retry = getService('retry');
describe('Configure Case', function () {
+ // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
+ this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
@@ -34,6 +36,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
describe('Closure options', function () {
+ // Error: Expected the radio group value to equal "close-by-pushing" (got "close-by-user")
+ this.tags(['failsOnMKI']);
it('defaults the closure option correctly', async () => {
await cases.common.assertRadioGroupValue('closure-options-radio-group', 'close-by-user');
});
diff --git a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/list_view.ts b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/list_view.ts
index e3d1d8408beff..045d56b9c5dd8 100644
--- a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/list_view.ts
+++ b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/list_view.ts
@@ -17,7 +17,9 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const svlSecNavigation = getService('svlSecNavigation');
const svlCommonPage = getPageObject('svlCommonPage');
- describe('Cases List', () => {
+ describe('Cases List', function () {
+ // multiple errors in after hook due to delete permission
+ this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
@@ -47,6 +49,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
describe('bulk actions', () => {
+ // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
+ // action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
describe('delete', () => {
createNCasesBeforeDeleteAllAfter(8, getPageObject, getService);
@@ -148,6 +152,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
describe('severity filtering', () => {
+ // Error: retry.tryForTime timeout: Error: expected 10 to equal 5
before(async () => {
await testSubjects.click('solutionSideNavItemLink-cases');
@@ -196,6 +201,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
describe('pagination', () => {
+ // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
createNCasesBeforeDeleteAllAfter(12, getPageObject, getService);
it('paginates cases correctly', async () => {