Skip to content

Commit

Permalink
[Discover] Context aware Single Document & Surrounding Documents pages (
Browse files Browse the repository at this point in the history
#190540)

- Closes #188178

## Summary

This PR makes sure to resolve profiles on:
- [x] Single document page (also added doc viewer extension support)
- [x] Surrounding documents page (also added cell renderes support)

### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: Davis McPhee <davis.mcphee@elastic.co>
  • Loading branch information
jughosta and davismcphee authored Sep 2, 2024
1 parent f2aba46 commit 0017463
Show file tree
Hide file tree
Showing 18 changed files with 465 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@
border-left: 0;
border-right: 0;
}
.euiDataGridRowCell.euiDataGridRowCell--controlColumn[data-gridcell-column-id='additionalRowControl_menuControl'] .euiDataGridRowCell__content {
padding-bottom: 0;
}

.euiDataGridHeaderCell.euiDataGridHeaderCell--controlColumn[data-gridcell-column-id='select'] {
padding-left: $euiSizeXS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
import type { HistoryLocationState } from '../../build_services';
import { createSearchSessionMock } from '../../__mocks__/search_session';
import { createDiscoverServicesMock } from '../../__mocks__/services';

const mockFilterManager = createFilterManagerMock();
const mockNavigationPlugin = {
ui: { TopNavMenu: mockTopNavMenu, AggregateQueryTopNavMenu: mockTopNavMenu },
};
const discoverServices = createDiscoverServicesMock();

describe('ContextApp test', () => {
const { history } = createSearchSessionMock();
Expand All @@ -53,6 +55,7 @@ describe('ContextApp test', () => {
toastNotifications: { addDanger: () => {} },
navigation: mockNavigationPlugin,
core: {
...discoverServices.core,
executionContext: {
set: jest.fn(),
},
Expand All @@ -75,6 +78,7 @@ describe('ContextApp test', () => {
},
contextLocator: { getRedirectUrl: jest.fn(() => '') },
singleDocLocator: { getRedirectUrl: jest.fn(() => '') },
profilesManager: discoverServices.profilesManager,
} as unknown as DiscoverServices;

const defaultProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { DocTableContext } from '../../components/doc_table/doc_table_context';
import { useDiscoverServices } from '../../hooks/use_discover_services';
import { DiscoverGridFlyout } from '../../components/discover_grid_flyout';
import { onResizeGridColumn } from '../../utils/on_resize_grid_column';
import { useProfileAccessor } from '../../context_awareness';

export interface ContextAppContentProps {
columns: string[];
Expand Down Expand Up @@ -159,6 +160,12 @@ export function ContextAppContent({
[grid, setAppState]
);

const getCellRenderersAccessor = useProfileAccessor('getCellRenderers');
const cellRenderers = useMemo(() => {
const getCellRenderers = getCellRenderersAccessor(() => ({}));
return getCellRenderers();
}, [getCellRenderersAccessor]);

return (
<Fragment>
<WrapperWithPadding>
Expand Down Expand Up @@ -222,6 +229,7 @@ export function ContextAppContent({
configHeaderRowHeight={3}
settings={grid}
onResize={onResize}
externalCustomRenderers={cellRenderers}
/>
</CellActionsProvider>
</div>
Expand Down
17 changes: 15 additions & 2 deletions src/plugins/discover/public/application/context/services/anchor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { lastValueFrom } from 'rxjs';
import { firstValueFrom, lastValueFrom } from 'rxjs';
import { i18n } from '@kbn/i18n';
import { ISearchSource, EsQuerySortValue } from '@kbn/data-plugin/public';
import type { DataView } from '@kbn/data-views-plugin/public';
Expand All @@ -14,6 +14,7 @@ import { buildDataTableRecord } from '@kbn/discover-utils';
import type { DataTableRecord, EsHitRecord } from '@kbn/discover-utils/types';
import type { SearchResponseWarning } from '@kbn/search-response-warnings';
import type { DiscoverServices } from '../../../build_services';
import { createDataViewDataSource } from '../../../../common/data_sources';

export async function fetchAnchor(
anchorId: string,
Expand All @@ -26,6 +27,16 @@ export async function fetchAnchor(
anchorRow: DataTableRecord;
interceptedWarnings: SearchResponseWarning[];
}> {
const { core, profilesManager } = services;

const solutionNavId = await firstValueFrom(core.chrome.getActiveSolutionNavId$());
await profilesManager.resolveRootProfile({ solutionNavId });
await profilesManager.resolveDataSourceProfile({
dataSource: dataView?.id ? createDataViewDataSource({ dataViewId: dataView.id }) : undefined,
dataView,
query: { query: '', language: 'kuery' },
});

updateSearchSource(searchSource, anchorId, sort, useNewFieldsApi, dataView);

const adapter = new RequestAdapter();
Expand Down Expand Up @@ -55,7 +66,9 @@ export async function fetchAnchor(
});

return {
anchorRow: buildDataTableRecord(doc, dataView, true),
anchorRow: profilesManager.resolveDocumentProfile({
record: buildDataTableRecord(doc, dataView, true),
}),
interceptedWarnings,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export async function fetchHitsInInterval(
rows: DataTableRecord[];
interceptedWarnings: SearchResponseWarning[];
}> {
const { profilesManager } = services;
const range: RangeQuery = {
format: 'strict_date_optional_time',
};
Expand Down Expand Up @@ -96,7 +97,11 @@ export async function fetchHitsInInterval(

const { rawResponse } = await lastValueFrom(fetch$);
const dataView = searchSource.getField('index');
const rows = rawResponse.hits?.hits.map((hit) => buildDataTableRecord(hit, dataView!));
const rows = rawResponse.hits?.hits.map((hit) =>
profilesManager.resolveDocumentProfile({
record: buildDataTableRecord(hit, dataView!),
})
);
const interceptedWarnings: SearchResponseWarning[] = [];
services.data.search.showWarnings(adapter, (warning) => {
interceptedWarnings.push(warning);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { setUnifiedDocViewerServices } from '@kbn/unified-doc-viewer-plugin/public/plugin';
import { mockUnifiedDocViewerServices } from '@kbn/unified-doc-viewer-plugin/public/__mocks__';
import type { UnifiedDocViewerServices } from '@kbn/unified-doc-viewer-plugin/public/types';
import { createDiscoverServicesMock } from '../../../__mocks__/services';

const discoverServices = createDiscoverServicesMock();
const mockSearchApi = jest.fn();

beforeEach(() => {
Expand Down Expand Up @@ -68,6 +70,8 @@ async function mountDoc(update = false) {
},
locator: { getUrl: jest.fn(() => Promise.resolve('mock-url')) },
chrome: { setBreadcrumbs: jest.fn() },
profilesManager: discoverServices.profilesManager,
core: discoverServices.core,
};
setUnifiedDocViewerServices({
...mockUnifiedDocViewerServices,
Expand Down
37 changes: 31 additions & 6 deletions src/plugins/discover/public/application/doc/components/doc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@
* Side Public License, v 1.
*/

import React, { useEffect } from 'react';
import React, { useCallback, useEffect } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { firstValueFrom } from 'rxjs';
import { EuiCallOut, EuiLink, EuiLoadingSpinner, EuiPage, EuiPageBody } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { ElasticRequestState } from '@kbn/unified-doc-viewer';
import { UnifiedDocViewer, useEsDocSearch } from '@kbn/unified-doc-viewer-plugin/public';
import { useEsDocSearch } from '@kbn/unified-doc-viewer-plugin/public';
import type { EsDocSearchProps } from '@kbn/unified-doc-viewer-plugin/public/types';
import { setBreadcrumbs } from '../../../utils/breadcrumbs';
import { useDiscoverServices } from '../../../hooks/use_discover_services';
import { SingleDocViewer } from './single_doc_viewer';
import { createDataViewDataSource } from '../../../../common/data_sources';

export interface DocProps extends EsDocSearchProps {
/**
Expand All @@ -25,11 +28,33 @@ export interface DocProps extends EsDocSearchProps {

export function Doc(props: DocProps) {
const { dataView } = props;
const [reqState, hit] = useEsDocSearch(props);
const services = useDiscoverServices();
const { locator, chrome, docLinks } = services;
const { locator, chrome, docLinks, core, profilesManager } = services;
const indexExistsLink = docLinks.links.apis.indexExists;

const onBeforeFetch = useCallback(async () => {
const solutionNavId = await firstValueFrom(core.chrome.getActiveSolutionNavId$());
await profilesManager.resolveRootProfile({ solutionNavId });
await profilesManager.resolveDataSourceProfile({
dataSource: dataView?.id ? createDataViewDataSource({ dataViewId: dataView.id }) : undefined,
dataView,
query: { query: '', language: 'kuery' },
});
}, [profilesManager, core, dataView]);

const onProcessRecord = useCallback(
(record) => {
return profilesManager.resolveDocumentProfile({ record });
},
[profilesManager]
);

const [reqState, record] = useEsDocSearch({
...props,
onBeforeFetch,
onProcessRecord,
});

useEffect(() => {
setBreadcrumbs({
services,
Expand Down Expand Up @@ -117,9 +142,9 @@ export function Doc(props: DocProps) {
</EuiCallOut>
)}

{reqState === ElasticRequestState.Found && hit !== null && dataView && (
{reqState === ElasticRequestState.Found && record !== null && dataView && (
<div data-test-subj="doc-hit">
<UnifiedDocViewer hit={hit} dataView={dataView} hideActionsColumn />
<SingleDocViewer record={record} dataView={dataView} />
</div>
)}
</EuiPageBody>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React, { useMemo } from 'react';
import { UnifiedDocViewer } from '@kbn/unified-doc-viewer-plugin/public';
import type { DocViewsRegistry } from '@kbn/unified-doc-viewer';
import type { DataTableRecord } from '@kbn/discover-utils';
import type { DataView } from '@kbn/data-views-plugin/public';
import { useProfileAccessor } from '../../../context_awareness';

interface SingleDocViewerProps {
record: DataTableRecord;
dataView: DataView;
}

export const SingleDocViewer: React.FC<SingleDocViewerProps> = ({ record, dataView }) => {
const getDocViewerAccessor = useProfileAccessor('getDocViewer', {
record,
});
const docViewer = useMemo(() => {
const getDocViewer = getDocViewerAccessor(() => ({
title: undefined,
docViewsRegistry: (registry: DocViewsRegistry) => registry,
}));

return getDocViewer({ record });
}, [getDocViewerAccessor, record]);

return (
<UnifiedDocViewer
hit={record}
dataView={dataView}
hideActionsColumn
docViewsRegistry={docViewer.docViewsRegistry}
/>
);
};
29 changes: 27 additions & 2 deletions src/plugins/unified_doc_viewer/public/hooks/use_es_doc_search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ export interface EsDocSearchProps {
* Records fetched from text based query
*/
textBasedHits?: DataTableRecord[];
/**
* An optional callback that will be called before fetching the doc
*/
onBeforeFetch?: () => Promise<void>;
/**
* An optional callback that will be called after fetching the doc
* @param record
*/
onProcessRecord?: (record: DataTableRecord) => DataTableRecord;
}

/**
Expand All @@ -50,6 +59,8 @@ export function useEsDocSearch({
dataView,
requestSource,
textBasedHits,
onBeforeFetch,
onProcessRecord,
}: EsDocSearchProps): [ElasticRequestState, DataTableRecord | null, () => void] {
const [status, setStatus] = useState(ElasticRequestState.Loading);
const [hit, setHit] = useState<DataTableRecord | null>(null);
Expand All @@ -63,6 +74,9 @@ export function useEsDocSearch({

const singleDocFetchingStartTime = window.performance.now();
try {
if (onBeforeFetch) {
await onBeforeFetch();
}
const result = await lastValueFrom(
data.search.search({
params: {
Expand All @@ -77,7 +91,8 @@ export function useEsDocSearch({

if (hits?.hits?.[0]) {
setStatus(ElasticRequestState.Found);
setHit(buildDataTableRecord(hits.hits[0], dataView));
const record = buildDataTableRecord(hits?.hits?.[0], dataView);
setHit(onProcessRecord ? onProcessRecord(record) : record);
} else {
setStatus(ElasticRequestState.NotFound);
}
Expand All @@ -98,7 +113,17 @@ export function useEsDocSearch({
duration: singleDocFetchingDuration,
});
}
}, [analytics, data.search, dataView, id, index, useNewFieldsApi, requestSource]);
}, [
analytics,
data.search,
dataView,
id,
index,
useNewFieldsApi,
requestSource,
onBeforeFetch,
onProcessRecord,
]);

useEffect(() => {
if (textBasedHits) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../ftr_provider_context';

export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['common', 'discover', 'unifiedFieldList']);
const PageObjects = getPageObjects(['common', 'discover', 'unifiedFieldList', 'header']);
const esArchiver = getService('esArchiver');
const testSubjects = getService('testSubjects');
const dataGrid = getService('dataGrid');
const dataViews = getService('dataViews');
const queryBar = getService('queryBar');
const browser = getService('browser');

describe('extension getCellRenderers', () => {
before(async () => {
Expand Down Expand Up @@ -76,8 +77,23 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await queryBar.submitQuery();
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.unifiedFieldList.clickFieldListItemAdd('log.level');
const firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 1);
const logLevelBadge = await firstCell.findByTestSubject('*logLevelBadgeCell-');
let firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 1);
let logLevelBadge = await firstCell.findByTestSubject('*logLevelBadgeCell-');
expect(await logLevelBadge.getVisibleText()).to.be('debug');
expect(await logLevelBadge.getComputedStyle('background-color')).to.be(
'rgba(190, 207, 227, 1)'
);

// check Surrounding docs page
await dataGrid.clickRowToggle();
const [, surroundingActionEl] = await dataGrid.getRowActions();
await surroundingActionEl.click();
await PageObjects.header.waitUntilLoadingHasFinished();
await browser.refresh();
await PageObjects.header.waitUntilLoadingHasFinished();

firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 1);
logLevelBadge = await firstCell.findByTestSubject('*logLevelBadgeCell-');
expect(await logLevelBadge.getVisibleText()).to.be('debug');
expect(await logLevelBadge.getComputedStyle('background-color')).to.be(
'rgba(190, 207, 227, 1)'
Expand All @@ -93,7 +109,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await queryBar.submitQuery();
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.unifiedFieldList.clickFieldListItemAdd('log.level');
const firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 1);
let firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 1);
expect(await firstCell.getVisibleText()).to.be('debug');
await testSubjects.missingOrFail('*logLevelBadgeCell-');

// check Surrounding docs page
await dataGrid.clickRowToggle();
const [, surroundingActionEl] = await dataGrid.getRowActions();
await surroundingActionEl.click();
await PageObjects.header.waitUntilLoadingHasFinished();
await browser.refresh();
await PageObjects.header.waitUntilLoadingHasFinished();

firstCell = await dataGrid.getCellElementExcludingControlColumns(1, 1);
expect(await firstCell.getVisibleText()).to.be('debug');
await testSubjects.missingOrFail('*logLevelBadgeCell-');
});
Expand Down
Loading

0 comments on commit 0017463

Please sign in to comment.