Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
6b3a426
Replace log stream component on transaction details page
achyutjhunjhunwala Apr 28, 2025
6158dbf
Merge branch 'main' into replace-log-stream-component
achyutjhunjhunwala Apr 30, 2025
5193c6b
Merge branch 'main' into replace-log-stream-component
achyutjhunjhunwala May 7, 2025
d197478
Merge branch 'main' into replace-log-stream-component
achyutjhunjhunwala May 26, 2025
0059886
Merge branch 'main' into replace-log-stream-component
achyutjhunjhunwala May 28, 2025
a9660b2
Fix passing long Discover locator inside execution context which was …
achyutjhunjhunwala May 28, 2025
635e477
Enable Flyout for all new Logs Saved Search Embeddables
achyutjhunjhunwala May 30, 2025
2eaaec5
Replace Log stream component from Host List page
achyutjhunjhunwala Jun 2, 2025
0e09b27
replace log stream in hosts detail view which in addition also impact…
achyutjhunjhunwala Jun 3, 2025
69fc87d
Merge branch 'main' into replace-log-stream-component
achyutjhunjhunwala Jun 3, 2025
145ac87
Fix the locator passing error
achyutjhunjhunwala Jun 3, 2025
e6586c2
Merge branch 'main' into replace-log-stream-component
achyutjhunjhunwala Jun 5, 2025
7d89fbc
[CI] Auto-commit changed files from 'node scripts/styled_components_m…
kibanamachine Jun 5, 2025
1cb50a2
Add new profile for handling Fleet Logs
achyutjhunjhunwala Jun 5, 2025
08439cf
[CI] Auto-commit changed files from 'ts-node .buildkite/pipeline-reso…
kibanamachine Jun 5, 2025
a4210d3
Fix quick check issues
achyutjhunjhunwala Jun 5, 2025
6e34237
Replace the newly added fleet profile with custom column logic
achyutjhunjhunwala Jun 6, 2025
1ceac5c
Add logic to merge all controls which open flyout under 1 flag and up…
achyutjhunjhunwala Jun 6, 2025
fbb66e3
Merge branch 'main' into replace-log-stream-component
achyutjhunjhunwala Jun 6, 2025
bbc425d
Fix Unit test for agent_logs
achyutjhunjhunwala Jun 6, 2025
fe79cb6
Fix initialize edit unit tests
achyutjhunjhunwala Jun 6, 2025
ecf91e7
Fix execution context browser ftr tests
achyutjhunjhunwala Jun 6, 2025
8ee9245
Revert Fix execution context browser ftr tests as the issue was with …
achyutjhunjhunwala Jun 6, 2025
e0f72a1
Fix initial state bug
achyutjhunjhunwala Jun 6, 2025
161e432
Fix Unit test case with fixed edit url logic
achyutjhunjhunwala Jun 6, 2025
923a74f
Fix profile test as the new condition to display leading columns has …
achyutjhunjhunwala Jun 6, 2025
ca3cc6e
Fix checktype issue
achyutjhunjhunwala Jun 6, 2025
6e841ce
[CI] Auto-commit changed files from 'node scripts/notice'
kibanamachine Jun 6, 2025
09455f8
Merge branch 'main' into replace-log-stream-component
achyutjhunjhunwala Jun 10, 2025
a214ce1
Fix infra FTR tests
achyutjhunjhunwala Jun 10, 2025
a5d5d51
Merge branch 'main' into replace-log-stream-component
achyutjhunjhunwala Jun 10, 2025
8f3e0ee
Merge branch 'main' into replace-log-stream-component
achyutjhunjhunwala Jun 10, 2025
4b36b7a
Fix a minor change
achyutjhunjhunwala Jun 11, 2025
736115f
Merge branch 'main' into replace-log-stream-component
achyutjhunjhunwala Jun 13, 2025
7e4ad3b
Fix review comments
achyutjhunjhunwala Jun 13, 2025
5e027c9
Merge branch 'main' into replace-log-stream-component
achyutjhunjhunwala Jun 13, 2025
002e550
Fix variable renaming
achyutjhunjhunwala Jun 13, 2025
39b702d
Merge branch 'main' into replace-log-stream-component
achyutjhunjhunwala Jun 13, 2025
ad5fe62
Fix a stupid merge issue which removed a comma
achyutjhunjhunwala Jun 13, 2025
812f748
Add Open in Discover links for pages missing them
achyutjhunjhunwala Jun 13, 2025
64627df
Fix checktype issues
achyutjhunjhunwala Jun 13, 2025
abd3cf1
Fix a missing linting issue
achyutjhunjhunwala Jun 13, 2025
195ef4b
Merge branch 'main' into replace-log-stream-component
achyutjhunjhunwala Jun 16, 2025
9634678
Fix fleet columns
achyutjhunjhunwala Jun 16, 2025
58094fb
Add logic to handle log stream dashboards from older versions to load…
achyutjhunjhunwala Jun 16, 2025
afd4f42
Merge branch 'main' into replace-log-stream-component
achyutjhunjhunwala Jun 17, 2025
466188d
Add logic to new legacy embeddable to handle the Logs Stream embeddab…
achyutjhunjhunwala Jun 18, 2025
9f1eaa1
Merge branch 'main' into replace-log-stream-component
achyutjhunjhunwala Jun 18, 2025
5b78f6c
Enable search bar in Service Logs tab
achyutjhunjhunwala Jun 18, 2025
8956519
Enable search bar in Service Logs tab for Mobile Services
achyutjhunjhunwala Jun 18, 2025
77a0656
Fix height for logs tab in infra
achyutjhunjhunwala Jun 18, 2025
c076e74
Revert enabling the search bar in APM
achyutjhunjhunwala Jun 18, 2025
7dbc569
Add missing search bar on Hosts page which was deleted by mistake
achyutjhunjhunwala Jun 19, 2025
d73255b
Merge branch 'main' into replace-log-stream-component
achyutjhunjhunwala Jun 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const DiscoverGrid: React.FC<DiscoverGridProps> = ({
rowAdditionalLeadingControls: customRowAdditionalLeadingControls,
...props
}) => {
const { dataView, setExpandedDoc } = props;
const { dataView, setExpandedDoc, renderDocumentView } = props;
const getRowIndicatorProvider = useProfileAccessor('getRowIndicatorProvider');
const getRowIndicator = useMemo(() => {
return getRowIndicatorProvider(() => undefined)({ dataView: props.dataView });
Expand All @@ -48,6 +48,7 @@ export const DiscoverGrid: React.FC<DiscoverGridProps> = ({
query,
updateESQLQuery: onUpdateESQLQuery,
setExpandedDoc,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
setExpandedDoc,
setExpandedDoc: renderDocumentView ? setExpandedDoc : undefined,

This was the area I meant in my original comment.

isDocViewerEnabled: !!renderDocumentView,
});
}, [
customRowAdditionalLeadingControls,
Expand All @@ -56,6 +57,7 @@ export const DiscoverGrid: React.FC<DiscoverGridProps> = ({
onUpdateESQLQuery,
query,
setExpandedDoc,
renderDocumentView,
]);

const getPaginationConfigAccessor = useProfileAccessor('getPaginationConfig');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const getRowAdditionalLeadingControls: LogsDataSourceProfileProvider['pro
(prev, { context }) =>
(params) => {
const additionalControls = prev(params) || [];
const { updateESQLQuery, query, setExpandedDoc } = params;
const { updateESQLQuery, query, setExpandedDoc, isDocViewerEnabled } = params;

const isDegradedDocsControlEnabled = isOfAggregateQueryType(query)
? queryContainsMetadataIgnored(query)
Expand Down Expand Up @@ -52,15 +52,17 @@ export const getRowAdditionalLeadingControls: LogsDataSourceProfileProvider['pro
setExpandedDoc(props.record, { initialTabId: 'doc_view_logs_overview' });
};

return [
...additionalControls,
createDegradedDocsControl({
enabled: isDegradedDocsControlEnabled,
addIgnoredMetadataToQuery,
onClick: leadingControlClick('quality_issues'),
}),
createStacktraceControl({ onClick: leadingControlClick('stacktrace') }),
];
return isDocViewerEnabled
? [
...additionalControls,
createDegradedDocsControl({
enabled: isDegradedDocsControlEnabled,
addIgnoredMetadataToQuery,
onClick: leadingControlClick('quality_issues'),
}),
createStacktraceControl({ onClick: leadingControlClick('stacktrace') }),
]
: additionalControls;
};

const queryContainsMetadataIgnored = (query: AggregateQuery) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,12 +254,29 @@ describe('logsDataSourceProfileProvider', () => {
});
const rowAdditionalLeadingControls = getRowAdditionalLeadingControls?.({
dataView: dataViewWithLogLevel,
isDocViewerEnabled: true,
});

expect(rowAdditionalLeadingControls).toHaveLength(2);
expect(rowAdditionalLeadingControls?.[0].id).toBe('connectedDegradedDocs');
expect(rowAdditionalLeadingControls?.[1].id).toBe('connectedStacktraceDocs');
});

it('should not return the passed additional controls if the flag is turned off', () => {
const getRowAdditionalLeadingControls =
logsDataSourceProfileProvider.profile.getRowAdditionalLeadingControls?.(() => undefined, {
context: {
category: DataSourceCategory.Logs,
logOverviewContext$: new BehaviorSubject<LogOverviewContext | undefined>(undefined),
},
});
const rowAdditionalLeadingControls = getRowAdditionalLeadingControls?.({
dataView: dataViewWithLogLevel,
isDocViewerEnabled: false,
});

expect(rowAdditionalLeadingControls).toHaveLength(0);
});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ export interface RowControlsExtensionParams {
* @param options.initialTabId - The tabId to display in the flyout
*/
setExpandedDoc?: (record?: DataTableRecord, options?: { initialTabId?: string }) => void;
/**
* Flag to indicate if Flyout opening controls must be rendered or not
*/
isDocViewerEnabled: boolean;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ export function SearchEmbeddableGridComponent({

// `api.query$` and `api.filters$` are the initial values from the saved search SO (as of now)
// `fetchContext.query` and `fetchContext.filters` are Dashboard's query and filters

const savedSearchQuery = apiQuery;
const savedSearchFilters = apiFilters;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export const SEARCH_EMBEDDABLE_CELL_ACTIONS_TRIGGER: Trigger = {
'This trigger is used to replace the cell actions for Discover session embeddable grid.',
} as const;

export const LEGACY_LOG_STREAM_EMBEDDABLE = 'LOG_STREAM_EMBEDDABLE';

export const ACTION_VIEW_SAVED_SEARCH = 'ACTION_VIEW_SAVED_SEARCH';

export const DEFAULT_HEADER_ROW_HEIGHT_LINES = 3;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import type { SavedSearch } from '@kbn/saved-search-plugin/public';
import type { DataView } from '@kbn/data-views-plugin/common';
import { getAllLogsDataViewSpec } from '@kbn/discover-utils/src';
import { toSavedSearchAttributes } from '@kbn/saved-search-plugin/common';
import { getSearchEmbeddableFactory } from './get_search_embeddable_factory';
import { LEGACY_LOG_STREAM_EMBEDDABLE } from './constants';

export const getLegacyLogStreamEmbeddableFactory = (
...[{ startServices, discoverServices }]: Parameters<typeof getSearchEmbeddableFactory>
) => {
const searchEmbeddableFactory = getSearchEmbeddableFactory({ startServices, discoverServices });
const logStreamEmbeddableFactory: ReturnType<typeof getSearchEmbeddableFactory> = {
type: LEGACY_LOG_STREAM_EMBEDDABLE,
buildEmbeddable: async ({ initialState, ...restParams }) => {
const searchSource = await discoverServices.data.search.searchSource.create();
let fallbackPattern = 'logs-*-*';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea with the fallback to ensure we always have an index pattern available 👍

// Given that the logDataAccess service is an optional dependency with discover, we need to check if it exists
if (discoverServices.logsDataAccess) {
fallbackPattern =
await discoverServices.logsDataAccess.services.logSourcesService.getFlattenedLogSources();
}

const spec = getAllLogsDataViewSpec({ allLogsIndexPattern: fallbackPattern });
const dataView: DataView = await discoverServices.data.dataViews.create(spec);

// Finally assign the data view to the search source
searchSource.setField('index', dataView);

const savedSearch: SavedSearch = {
title: initialState.rawState.title,
description: initialState.rawState.description,
timeRange: initialState.rawState.timeRange,
sort: initialState.rawState.sort ?? [],
columns: initialState.rawState.columns ?? [],
searchSource,
managed: false,
};
const { searchSourceJSON, references } = searchSource.serialize();

initialState = {
...initialState,
rawState: {
...initialState.rawState,
attributes: {
...toSavedSearchAttributes(savedSearch, searchSourceJSON),
references,
},
},
};

return searchEmbeddableFactory.buildEmbeddable({ initialState, ...restParams });
},
};

return logStreamEmbeddableFactory;
};
Original file line number Diff line number Diff line change
Expand Up @@ -34,85 +34,112 @@ describe('initialize edit api', () => {
const waitOneTick = () => new Promise((resolve) => setTimeout(resolve, 0));

describe('get app target', () => {
const runEditLinkTest = async (dataView?: DataView, byValue?: boolean) => {
jest
.spyOn(discoverServiceMock.locator, 'getUrl')
.mockClear()
.mockResolvedValueOnce('/base/mock-url');
jest
.spyOn(discoverServiceMock.core.http.basePath, 'remove')
.mockClear()
.mockReturnValueOnce('/mock-url');

if (dataView) {
mockedApi.dataViews$.next([dataView]);
const runEditLinkTest = async (dataViewInput?: DataView, byValue?: boolean) => {
const currentDataView = dataViewInput || dataViewMock;
// Determine if the current scenario will use a redirect
const currentSavedObjectId = byValue ? undefined : 'test-id';
const isDataViewPersisted = currentDataView.isPersisted
? currentDataView.isPersisted()
: true; // Assume persisted if method undefined
const useRedirect = !currentSavedObjectId && !isDataViewPersisted;

if (useRedirect) {
// This is the "by value with ad hoc data view" (redirect) case.
jest
.spyOn(discoverServiceMock.locator, 'getUrl')
.mockClear()
.mockResolvedValueOnce('/base/state-url-for-redirect'); // For urlWithoutLocationState
jest
.spyOn(discoverServiceMock.core.http.basePath, 'remove')
.mockClear()
.mockReturnValueOnce('/mock-url'); // For editPath (applied to getRedirectUrl result)
} else {
mockedApi.dataViews$.next([dataViewMock]);
}
if (byValue) {
mockedApi.savedObjectId$.next(undefined);
} else {
mockedApi.savedObjectId$.next('test-id');
// This is a "by reference" or "by value with persisted data view" (non-redirect) case.
jest
.spyOn(discoverServiceMock.locator, 'getUrl')
.mockClear()
.mockResolvedValueOnce('/base/discover-home') // For getUrl({}) -> urlWithoutLocationState
.mockResolvedValueOnce('/base/mock-url'); // For getUrl(locatorParams) -> raw editUrl
jest
.spyOn(discoverServiceMock.core.http.basePath, 'remove')
.mockClear()
.mockReturnValueOnce('/mock-url'); // For remove('/base/mock-url') -> editPath
}

mockedApi.dataViews$.next([currentDataView]);
mockedApi.savedObjectId$.next(currentSavedObjectId);

await waitOneTick();

const {
path: editPath,
app: editApp,
editUrl,
urlWithoutLocationState,
} = await getAppTarget(mockedApi, discoverServiceMock);

return { editPath, editApp, editUrl };
return { editPath, editApp, editUrl, urlWithoutLocationState };
};

const testByReference = ({
const testByReferenceOrNonRedirectValue = ({
editPath,
editApp,
editUrl,
urlWithoutLocationState,
}: {
editPath: string;
editApp: string;
editUrl: string;
urlWithoutLocationState: string;
}) => {
const locatorParams = getDiscoverLocatorParams(mockedApi);
expect(discoverServiceMock.locator.getUrl).toHaveBeenCalledTimes(1);
expect(discoverServiceMock.locator.getUrl).toHaveBeenCalledWith(locatorParams);
expect(discoverServiceMock.locator.getUrl).toHaveBeenCalledTimes(2);
expect(discoverServiceMock.locator.getUrl).toHaveBeenCalledWith({}); // For urlWithoutLocationState
expect(discoverServiceMock.locator.getUrl).toHaveBeenCalledWith(locatorParams); // For raw editUrl

expect(discoverServiceMock.core.http.basePath.remove).toHaveBeenCalledTimes(1);
expect(discoverServiceMock.core.http.basePath.remove).toHaveBeenCalledWith('/base/mock-url');

expect(editApp).toBe('discover');
expect(editPath).toBe('/mock-url');
expect(editUrl).toBe('/base/mock-url');
expect(editPath).toBe('/mock-url'); // Result of basePath.remove
expect(editUrl).toBe('/base/mock-url'); // Raw editUrl before basePath.remove
expect(urlWithoutLocationState).toBe('/base/discover-home');
};

it('should correctly output edit link params for by reference saved search', async () => {
const { editPath, editApp, editUrl } = await runEditLinkTest();
testByReference({ editPath, editApp, editUrl });
const result = await runEditLinkTest(dataViewMock, false);
testByReferenceOrNonRedirectValue(result);
});

it('should correctly output edit link params for by reference saved search with ad hoc data view', async () => {
const { editPath, editApp, editUrl } = await runEditLinkTest(dataViewAdHoc);
testByReference({ editPath, editApp, editUrl });
// Still "by reference" (savedObjectId exists), so no redirect even with ad-hoc data view.
const result = await runEditLinkTest(dataViewAdHoc, false);
testByReferenceOrNonRedirectValue(result);
});

it('should correctly output edit link params for by value saved search', async () => {
const { editPath, editApp, editUrl } = await runEditLinkTest(undefined, true);
testByReference({ editPath, editApp, editUrl });
it('should correctly output edit link params for by value saved search (with persisted data view)', async () => {
// "by value" but with a persisted data view (dataViewMock), so no redirect.
const result = await runEditLinkTest(dataViewMock, true);
testByReferenceOrNonRedirectValue(result);
});

it('should correctly output edit link params for by value saved search with ad hoc data view', async () => {
// This specific test case mocks getRedirectUrl because it's unique to the redirect flow
jest
.spyOn(discoverServiceMock.locator, 'getRedirectUrl')
.mockClear()
.mockReturnValueOnce('/base/mock-url');
jest
.spyOn(discoverServiceMock.core.http.basePath, 'remove')
.mockClear()
.mockReturnValueOnce('/mock-url');
.mockReturnValueOnce('/base/mock-url'); // This will be the raw editUrl

const { editPath, editApp, editUrl } = await runEditLinkTest(dataViewAdHoc, true);
const result = await runEditLinkTest(dataViewAdHoc, true);
const { editPath, editApp, editUrl, urlWithoutLocationState } = result;

const locatorParams = getDiscoverLocatorParams(mockedApi);

// Assertions for urlWithoutLocationState part (getUrl({}))
expect(discoverServiceMock.locator.getUrl).toHaveBeenCalledTimes(1);
expect(discoverServiceMock.locator.getUrl).toHaveBeenCalledWith({});

// Assertions for redirect part (getRedirectUrl and basePath.remove)
expect(discoverServiceMock.locator.getRedirectUrl).toHaveBeenCalledTimes(1);
expect(discoverServiceMock.locator.getRedirectUrl).toHaveBeenCalledWith(locatorParams);
expect(discoverServiceMock.core.http.basePath.remove).toHaveBeenCalledTimes(1);
Expand All @@ -121,6 +148,7 @@ describe('initialize edit api', () => {
expect(editApp).toBe('r');
expect(editPath).toBe('/mock-url');
expect(editUrl).toBe('/base/mock-url');
expect(urlWithoutLocationState).toBe('/base/state-url-for-redirect');
});
});

Expand All @@ -130,13 +158,26 @@ describe('initialize edit api', () => {
navigateToEditor: mockedNavigate,
}));
mockedApi.dataViews$.next([dataViewMock]);
mockedApi.savedObjectId$.next('test-id'); // Assuming a by-reference scenario for onEdit
await waitOneTick();

// Mocking for getAppTarget call within onEdit
// Assuming a non-redirect case for simplicity
jest
.spyOn(discoverServiceMock.locator, 'getUrl')
.mockClear()
.mockResolvedValueOnce('/base/discover-home-for-onedit') // For getUrl({})
.mockResolvedValueOnce('/base/mock-url-for-onedit'); // For getUrl(locatorParams)
jest
.spyOn(discoverServiceMock.core.http.basePath, 'remove')
.mockClear()
.mockReturnValueOnce('/mock-url-for-onedit');

const { onEdit } = initializeEditApi({
uuid: 'test',
parentApi: {
getAppContext: jest.fn().mockResolvedValue({
getCurrentPath: jest.fn(),
getAppContext: jest.fn().mockReturnValue({
getCurrentPath: jest.fn().mockReturnValue('/current-parent-path'),
currentAppId: 'dashboard',
}),
},
Expand All @@ -148,8 +189,12 @@ describe('initialize edit api', () => {
await onEdit();
expect(mockedNavigate).toBeCalledTimes(1);
expect(mockedNavigate).toBeCalledWith('discover', {
path: '/mock-url',
state: expect.any(Object),
path: '/mock-url-for-onedit',
state: expect.objectContaining({
embeddableId: 'test',
originatingApp: 'dashboard',
originatingPath: '/current-parent-path',
}),
});
});
});
Loading
Loading