Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport 2.x] feat: enable data grid in Chatbot (#1341) #1383

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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 @@ -11,6 +11,7 @@
EuiDescriptionListDescription,
EuiDescriptionListTitle,
EuiPanel,
EuiDataGridProps,
} from '@elastic/eui';
import moment from 'moment';
import React, { Fragment, MutableRefObject, useEffect, useRef, useState } from 'react';
Expand All @@ -27,21 +28,24 @@
import { redoQuery } from '../../utils/utils';
import { FlyoutButton } from './docViewRow';

interface DataGridProps {
export interface DataGridProps {
http: HttpSetup;
pplService: PPLService;
rows: any[];

Check warning on line 34 in public/components/event_analytics/explorer/events_views/data_grid.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
rowsAll: any[];
explorerFields: IExplorerFields;
timeStampField: string;
rawQuery: string;
totalHits: number;
requestParams: any;

Check warning on line 39 in public/components/event_analytics/explorer/events_views/data_grid.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
startTime: string;
endTime: string;
storedSelectedColumns: IField[];
formatGridColumn?: (columns: EuiDataGridColumn[]) => EuiDataGridColumn[];
OuiDataGridProps?: Partial<EuiDataGridProps>;
}

const defaultFormatGrid = (columns: EuiDataGridColumn[]) => columns;

export function DataGrid(props: DataGridProps) {
const {
http,
Expand All @@ -54,6 +58,8 @@
requestParams,
startTime,
endTime,
formatGridColumn = defaultFormatGrid,
OuiDataGridProps,
} = props;
const { fetchEvents } = useFetchEvents({
pplService,
Expand Down Expand Up @@ -120,7 +126,7 @@
});
}
});
return columns;
return formatGridColumn(columns);
};

// used for which columns are visible and their order
Expand Down Expand Up @@ -258,6 +264,7 @@
showStyleSelector: false,
}}
rowHeightsOptions={rowHeightsOptions()}
{...OuiDataGridProps}
/>
</div>
</EuiPanel>
Expand Down
7 changes: 4 additions & 3 deletions public/components/event_analytics/utils/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@
};

// Get config objects according to specific editor
export const fetchConfigObject = (editor: string, propsOptions: any) => {

Check warning on line 192 in public/components/event_analytics/utils/utils.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
switch (editor) {
case 'Tooltip':
return {
Expand Down Expand Up @@ -285,14 +285,14 @@
const span = getSpanValue(groupByToken);
return {
[AGGREGATIONS]: statsToken.aggregations.map(
(agg: { [x: string]: any; function: { value_expression: any; name: any } }) => ({

Check warning on line 288 in public/components/event_analytics/utils/utils.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

Check warning on line 288 in public/components/event_analytics/utils/utils.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

Check warning on line 288 in public/components/event_analytics/utils/utils.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
label: agg.function?.value_expression,
name: agg.function?.value_expression,
aggregation: agg.function?.name,
[CUSTOM_LABEL]: agg[CUSTOM_LABEL as keyof StatsAggregationChunk],
})
),
[GROUPBY]: groupByToken?.group_fields?.map((agg: { [x: string]: any; name: any }) => ({

Check warning on line 295 in public/components/event_analytics/utils/utils.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

Check warning on line 295 in public/components/event_analytics/utils/utils.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
label: agg.name ?? '',
name: agg.name ?? '',
[CUSTOM_LABEL]: agg[CUSTOM_LABEL as keyof GroupField] ?? '',
Expand Down Expand Up @@ -414,18 +414,19 @@
timeStampField: string,
sortingFields: MutableRefObject<EuiDataGridSorting['columns']>,
pageFields: MutableRefObject<number[]>,
fetchEvents: any,

Check warning on line 417 in public/components/event_analytics/utils/utils.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
setData: React.Dispatch<React.SetStateAction<any[]>>

Check warning on line 418 in public/components/event_analytics/utils/utils.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
) => {
let finalQuery = '';

const start = datemath.parse(startTime)?.utc().format(DATE_PICKER_FORMAT);
const end = datemath.parse(endTime, { roundUp: true })?.utc().format(DATE_PICKER_FORMAT);
const tokens = rawQuery.replaceAll(PPL_NEWLINE_REGEX, '').match(PPL_INDEX_INSERT_POINT_REGEX);
const timeRange = timeStampField
? `| where ${timeStampField} >= '${start}' and ${timeStampField} <= '${end}'`
: '';

finalQuery = `${tokens![1]}=${
tokens![2]
} | where ${timeStampField} >= '${start}' and ${timeStampField} <= '${end}'`;
finalQuery = `${tokens![1]}=${tokens![2]} ${timeRange}`;

finalQuery += tokens![3];

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<DataGridContainer /> should render empty when rawQuery is not provided 1`] = `<div />`;

exports[`<DataGridContainer /> should render when all props are provided 1`] = `
<div>
<div
style="overflow-x: hidden;"
>
<div
data-test-subj="test"
>
{
"http": {},
"pplService": {},
"rows": [
{
"timestamp": "now",
"count": 1
}
],
"explorerFields": {
"selectedFields": [],
"unselectedFields": [],
"availableFields": [],
"queriedFields": []
},
"timeStampField": "timestamp",
"totalHits": 1,
"requestParams": {
"tabId": "OBSERVABILITY_DEFAULT_TAB"
},
"startTime": "",
"endTime": "now",
"storedSelectedColumns": [],
"OUIDataGridProps": {
"gridStyle": {
"fontSize": "s",
"cellPadding": "s"
},
"minSizeForControls": 300,
"rowHeightsOptions": {
"defaultHeight": 80
}
},
"rawQuery": "source=foo",
"columns": [
{
"id": "timestamp",
"isSortable": true,
"display": "Time",
"schema": "datetime",
"initialWidth": 50
},
{
"id": "_source",
"isSortable": false,
"display": "Source",
"schema": "_source"
}
]
}
</div>
</div>
</div>
`;

exports[`<DataGridContainer /> should switch props when in full screen mode 1`] = `
<div>
<div
style="overflow-x: hidden;"
>
<div
data-test-subj="test"
>
{
"http": {},
"pplService": {},
"rows": [
{
"timestamp": "now",
"count": 1
}
],
"explorerFields": {
"selectedFields": [],
"unselectedFields": [],
"availableFields": [],
"queriedFields": []
},
"timeStampField": "timestamp",
"totalHits": 1,
"requestParams": {
"tabId": "OBSERVABILITY_DEFAULT_TAB"
},
"startTime": "",
"endTime": "now",
"storedSelectedColumns": [],
"OUIDataGridProps": {
"gridStyle": {},
"minSizeForControls": 300,
"rowHeightsOptions": {}
},
"rawQuery": "source=foo",
"columns": [
{
"id": "timestamp",
"isSortable": true,
"display": "Time",
"schema": "datetime",
"initialWidth": 200
},
{
"id": "_source",
"isSortable": false,
"display": "Source",
"schema": "_source"
}
]
}
</div>
</div>
</div>
`;
122 changes: 122 additions & 0 deletions public/dependencies/components/data_grid_container.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { render, waitFor } from '@testing-library/react';
import { Provider } from 'react-redux';
import { DataGridContainer } from './data_grid_container';
import { RenderProps } from '../../types';
import { store } from '../../framework/redux/store';
import { DataGridProps } from '../../components/event_analytics/explorer/events_views/data_grid';
import {
DEFAULT_SOURCE_COLUMN as mock_DEFAULT_SOURCE_COLUMN,
DEFAULT_TIMESTAMP_COLUMN as mock_DEFAULT_TIMESTAMP_COLUMN,
} from '../../../common/constants/explorer';

jest.mock('../../components/event_analytics/utils/utils', () => ({
redoQuery: (...args: any[]) => {
args[7]([
{
timestamp: 'now',
count: 1,
},
]);
},
}));

jest.mock('../../../common/utils', () => ({
getPPLService: () => ({}),
getOSDHttp: () => ({}),
}));

jest.mock('../../components/event_analytics/explorer/events_views/data_grid', () => ({
DataGrid: (props: DataGridProps) => {
const columnsResult = props.formatGridColumn?.([
mock_DEFAULT_TIMESTAMP_COLUMN,
mock_DEFAULT_SOURCE_COLUMN,
]);
return (
<div data-test-subj="test">
{JSON.stringify({ ...props, columns: columnsResult }, null, 2)}
</div>
);
},
}));

describe('<DataGridContainer />', () => {
it('should render when all props are provided', async () => {
const { container, findByTestId } = render(
<Provider store={store}>
<DataGridContainer
rawQuery="source=foo"
renderProps={{
props: {
message: {
type: 'output',
contentType: 'ppl_data_grid',
content: 'source=foo',
},
},
chatContext: {
flyoutFullScreen: false,
} as RenderProps['chatContext'],
}}
/>
</Provider>
);
await findByTestId('test');
expect(container).toMatchSnapshot();
});

it('should render empty when rawQuery is not provided', async () => {
const { container, queryByTestId } = render(
<Provider store={store}>
<DataGridContainer
rawQuery=""
renderProps={{
props: {
message: {
type: 'output',
contentType: 'ppl_data_grid',
content: 'source=foo',
},
},
chatContext: {
flyoutFullScreen: false,
} as RenderProps['chatContext'],
}}
/>
</Provider>
);
await waitFor(() => {
expect(queryByTestId('test')).toBeNull();
});
expect(container).toMatchSnapshot();
});

it('should switch props when in full screen mode', async () => {
const { container, findByTestId } = render(
<Provider store={store}>
<DataGridContainer
rawQuery="source=foo"
renderProps={{
props: {
message: {
type: 'output',
contentType: 'ppl_data_grid',
content: 'source=foo',
},
},
chatContext: {
flyoutFullScreen: true,
} as RenderProps['chatContext'],
}}
/>
</Provider>
);
await findByTestId('test');
expect(container).toMatchSnapshot();
});
});
Loading
Loading