From 70a855c0a0a16780a91e129a3cf777d6511ba23d Mon Sep 17 00:00:00 2001 From: Shenoy Pratik Date: Tue, 16 Nov 2021 11:04:17 -0800 Subject: [PATCH] Panels bug fix4 (#249) * minor bug fix and updated unit tests Signed-off-by: Shenoy Pratik * fixed date change issue Signed-off-by: Shenoy Pratik * renamed relevance function to full text search Signed-off-by: Shenoy Pratik * duplicate search text removed Signed-off-by: Shenoy Pratik --- .../{relevance.ts => full_text_search.ts} | 22 +- .../helpers/ppl_docs/functions/index.ts | 11 +- .../common/helpers/ppl_docs/groups.tsx | 6 +- .../custom_panel_table.test.tsx.snap | 634 ++- .../custom_panel_view.test.tsx.snap | 4738 +++++++++++++++++ .../__tests__/custom_panel_table.test.tsx | 40 +- .../__tests__/custom_panel_view.test.tsx | 103 + .../custom_panels/custom_panel_view.tsx | 52 +- .../custom_panels/helpers/utils.tsx | 1 - .../panel_modules/panel_grid/panel_grid.tsx | 20 +- .../visualization_container.test.tsx.snap | 6 +- .../visualization_container.scss | 7 + .../visualization_container.tsx | 2 +- .../test/panels_constants.tsx | 21 +- 14 files changed, 5568 insertions(+), 95 deletions(-) rename dashboards-observability/public/components/common/helpers/ppl_docs/functions/{relevance.ts => full_text_search.ts} (77%) create mode 100644 dashboards-observability/public/components/custom_panels/__tests__/__snapshots__/custom_panel_view.test.tsx.snap create mode 100644 dashboards-observability/public/components/custom_panels/__tests__/custom_panel_view.test.tsx diff --git a/dashboards-observability/public/components/common/helpers/ppl_docs/functions/relevance.ts b/dashboards-observability/public/components/common/helpers/ppl_docs/functions/full_text_search.ts similarity index 77% rename from dashboards-observability/public/components/common/helpers/ppl_docs/functions/relevance.ts rename to dashboards-observability/public/components/common/helpers/ppl_docs/functions/full_text_search.ts index 1e402a0f1..4704df6ad 100644 --- a/dashboards-observability/public/components/common/helpers/ppl_docs/functions/relevance.ts +++ b/dashboards-observability/public/components/common/helpers/ppl_docs/functions/full_text_search.ts @@ -3,16 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -export const relevanceFunction = `## Relevance +export const fullTextSearchFunction = `## Full Text Search --- -The relevance based functions enable users to search the index for -documents by the relevance of the input query. The functions are built +The full text search based functions enable users to search the index for +documents by the full text search of the input query. The functions are built on the top of the search queries of the OpenSearch engine, but in memory execution within the plugin is not supported. These functions are able to perform the global filter of a query, for example the condition expression in a \`WHERE\` clause or in a \`HAVING\` clause. For more details -of the relevance based search, check out the design here: [Relevance +of the full text search based search, check out the design here: [Relevance Based Search With SQL/PPL Query Engine](https://github.com/opensearch-project/sql/issues/182) @@ -64,19 +64,19 @@ parameters: ### Limitations -The relevance functions are available to execute only in OpenSearch DSL -but not in memory as of now, so the relevance search might fail for -queries that are too complex to translate into DSL if the relevance +The full text search functions are available to execute only in OpenSearch DSL +but not in memory as of now, so the full text search might fail for +queries that are too complex to translate into DSL if the full text search function is following after a complex PPL query. To make your queries -always work-able, it is recommended to place the relevance commands as -close to the search command as possible, to ensure the relevance +always work-able, it is recommended to place the full text search commands as +close to the search command as possible, to ensure the full text search functions are eligible to push down. For example, a complex query like \`search source = people | rename firstname as name | dedup account_number | fields name, account_number, balance, employer | where match(employer, 'Open Search') | stats count() by city\` could fail because it is difficult to translate to DSL, but it would be better if we rewrite it to an equivalent query as \`search source = people | where match(employer, 'Open Search') | rename firstname as name | dedup account_number | fields name, account_number, balance, employer | stats count() by city\` -by moving the where command with relevance function to the second -command right after the search command, and the relevance would be +by moving the where command with full text search function to the second +command right after the search command, and the full text search would be optimized and executed smoothly in OpenSearch DSL. See [Optimization](https://github.com/opensearch-project/sql/blob/22924b13d9cb46759c8d213a7ce903effe06ab47/docs/user/optimization/optimization.rst) to get more details about the query engine optimization. `; diff --git a/dashboards-observability/public/components/common/helpers/ppl_docs/functions/index.ts b/dashboards-observability/public/components/common/helpers/ppl_docs/functions/index.ts index 625b2f6af..e061c6188 100644 --- a/dashboards-observability/public/components/common/helpers/ppl_docs/functions/index.ts +++ b/dashboards-observability/public/components/common/helpers/ppl_docs/functions/index.ts @@ -3,9 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ - -export {datetimeFunction} from './datetime'; -export {conditionFunction} from './condition'; -export {mathFunction} from './math'; -export {relevanceFunction} from './relevance'; -export {stringFunction} from './string'; \ No newline at end of file +export { datetimeFunction } from './datetime'; +export { conditionFunction } from './condition'; +export { mathFunction } from './math'; +export { stringFunction } from './string'; +export { fullTextSearchFunction } from './full_text_search'; diff --git a/dashboards-observability/public/components/common/helpers/ppl_docs/groups.tsx b/dashboards-observability/public/components/common/helpers/ppl_docs/groups.tsx index 230943586..962debf7b 100644 --- a/dashboards-observability/public/components/common/helpers/ppl_docs/groups.tsx +++ b/dashboards-observability/public/components/common/helpers/ppl_docs/groups.tsx @@ -22,7 +22,7 @@ import { datetimeFunction, stringFunction, conditionFunction, - relevanceFunction, + fullTextSearchFunction, } from './functions'; import { pplDatatypes, pplIdentifiers } from './language_structure'; @@ -100,8 +100,8 @@ export const Group2 = { value: conditionFunction, }, { - label: 'Relevance', - value: relevanceFunction, + label: 'Full Text Search', + value: fullTextSearchFunction, }, ], }; diff --git a/dashboards-observability/public/components/custom_panels/__tests__/__snapshots__/custom_panel_table.test.tsx.snap b/dashboards-observability/public/components/custom_panels/__tests__/__snapshots__/custom_panel_table.test.tsx.snap index 09adaa723..eef8d3fae 100644 --- a/dashboards-observability/public/components/custom_panels/__tests__/__snapshots__/custom_panel_table.test.tsx.snap +++ b/dashboards-observability/public/components/custom_panels/__tests__/__snapshots__/custom_panel_table.test.tsx.snap @@ -1,5 +1,578 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Panels Table Component renders empty panel table container 1`] = ` + +
+ +
+ +
+ +
+ +
+ +

+ Operational panels +

+
+
+
+
+
+ + +
+ +
+ +
+ +

+ Panels + + ( + 0 + ) + +

+
+ +
+ + +
+ +
+ Use Operational panels to create and view different visualizations on ingested observability data, using Piped Processing Language queries. + + + + Learn more + + + + + + + + + +
+
+
+
+
+ + +
+ +
+ +
+ + Actions + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + > + +
+
+ + + + + +
+
+
+
+
+
+ +
+ + + + + +
+
+
+
+
+
+
+ + +
+
+ +
+ + +
+ +
+

+ No Operational Panels +

+ +
+ + +
+ +
+ Use operational panels to dive deeper into observability +
+ using PPL queries and insightful visualizations +
+
+
+
+
+ +
+ + +
+ + +
+ +
+ + + + + +
+
+ +
+ + + + + +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +`; + exports[`Panels Table Component renders panel table container 1`] = ` - - + > + + + @@ -323,16 +898,16 @@ exports[`Panels Table Component renders panel table container 1`] = ` size="m" type="arrowDown" > - - + > + + + - - + > + + + - - + > + + + +
+ +
+ +
+ +
+ +
+ +

+ + +
+ +
+ +
+ + Created on + Invalid date +
+ + +
+ +
+ +
+ + + + + +
+
+ +
+ + Panel actions + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + withTitle={true} + > + +
+
+ + + + + +
+
+
+
+
+
+ +
+ + Add Visualization + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="addVisualizationContextMenu" + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + > + +
+
+ + + + + +
+
+
+
+
+
+
+
+
+
+

+ + +
+ +
+ +
+ + +
+
+ + + + +
+
+
+
+
+
+ +
+ + + +
+
+ +
+ + +
+ +
+ + } + > +
+ + + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="QuickSelectPopover" + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > + +
+
+ + + +
+
+
+
+
+
+ } + iconType={false} + isCustom={true} + startDateControl={
} + > +
+ +
+ + +
+
+ +
+ + +
+ + + + + + + + + + + +
+
+
+ + +
+ +
+ + +
+ + +
+ +
+ + +
+ +
+

+ Start by adding your first visualization +

+ +
+ + +
+ +
+ Use PPL Queries to fetch & filter Observability Data and Create Visualizations +
+
+
+
+
+ +
+ + +
+ + +
+ +
+ + Add Visualization + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="addVisualizationContextMenu" + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + > + +
+
+ + + + + +
+
+
+
+
+
+
+
+ +
+ +
+ + + + + +
+ + + + +
+ +
+ +
+ +
+ +`; + +exports[`Panels View Component renders panel view container with visualizations 2`] = ` + +
+ +
+ +
+ +
+ +
+ +

+ + +
+ +
+ +
+ + Created on + Invalid date +
+ + +
+ +
+ +
+ + + + + +
+
+ +
+ + Panel actions + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + withTitle={true} + > + +
+
+ + + + + +
+
+
+
+
+
+ +
+ + Add Visualization + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="addVisualizationContextMenu" + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + > + +
+
+ + + + + +
+
+
+
+
+
+
+
+
+
+

+ + +
+ +
+ +
+ + +
+
+ + + + +
+
+
+
+
+
+ +
+ + + +
+
+ +
+ + +
+ +
+ + } + > +
+ + + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="QuickSelectPopover" + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > + +
+
+ + + +
+
+
+
+
+
+ } + iconType={false} + isCustom={true} + startDateControl={
} + > +
+ +
+ + +
+
+ +
+ + +
+ + + + + + + + + + + +
+
+
+ + +
+ +
+ + +
+ + +
+ +
+ + +
+ +
+

+ Start by adding your first visualization +

+ +
+ + +
+ +
+ Use PPL Queries to fetch & filter Observability Data and Create Visualizations +
+
+
+
+
+ +
+ + +
+ + +
+ +
+ + Add Visualization + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="addVisualizationContextMenu" + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + > + +
+
+ + + + + +
+
+
+
+
+
+
+
+ +
+ +
+ + + + + +
+ + + + +
+ +
+ +
+ +
+ +`; + +exports[`Panels View Component renders panel view container without visualizations 1`] = ` + +
+ +
+ +
+ +
+ +
+ +

+ + +
+ +
+ +
+ + Created on + Invalid date +
+ + +
+ +
+ +
+ + + + + +
+
+ +
+ + Panel actions + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + withTitle={true} + > + +
+
+ + + + + +
+
+
+
+
+
+ +
+ + Add Visualization + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="addVisualizationContextMenu" + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + > + +
+
+ + + + + +
+
+
+
+
+
+
+
+
+
+

+ + +
+ +
+ +
+ + +
+
+ + + + +
+
+
+
+
+
+ +
+ + + +
+
+ +
+ + +
+ +
+ + } + > +
+ + + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="QuickSelectPopover" + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > + +
+
+ + + +
+
+
+
+
+
+ } + iconType={false} + isCustom={true} + startDateControl={
} + > +
+ +
+ + +
+
+ +
+ + +
+ + + + + + + + + + + +
+
+
+ + +
+ +
+ + +
+ + +
+ +
+ + +
+ +
+

+ Start by adding your first visualization +

+ +
+ + +
+ +
+ Use PPL Queries to fetch & filter Observability Data and Create Visualizations +
+
+
+
+
+ +
+ + +
+ + +
+ +
+ + Add Visualization + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="addVisualizationContextMenu" + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + > + +
+
+ + + + + +
+
+
+
+
+
+
+
+ +
+ +
+ + + + + +
+ + + + +
+ +
+ +
+ +
+ +`; diff --git a/dashboards-observability/public/components/custom_panels/__tests__/custom_panel_table.test.tsx b/dashboards-observability/public/components/custom_panels/__tests__/custom_panel_table.test.tsx index 399b677ad..6ff11f14e 100644 --- a/dashboards-observability/public/components/custom_panels/__tests__/custom_panel_table.test.tsx +++ b/dashboards-observability/public/components/custom_panels/__tests__/custom_panel_table.test.tsx @@ -8,22 +8,52 @@ import Adapter from 'enzyme-adapter-react-16'; import React from 'react'; import { CustomPanelTable } from '../custom_panel_table'; import { waitFor } from '@testing-library/react'; -import { panelsData } from '../../../../test/panels_constants'; +import { panelBreadCrumbs, panelsData } from '../../../../test/panels_constants'; import { CustomPanelListType } from '../../../../common/types/custom_panels'; describe('Panels Table Component', () => { configure({ adapter: new Adapter() }); + it('renders empty panel table container', async () => { + const loading = false; + const fetchCustomPanels = jest.fn(); + const customPanelData: CustomPanelListType[] = []; + const createCustomPanel = jest.fn(); + const setBreadcrumbs = jest.fn(); + const parentBreadcrumb = panelBreadCrumbs; + const renameCustomPanel = jest.fn(); + const cloneCustomPanel = jest.fn(); + const deleteCustomPanelList = jest.fn(); + const addSamplePanels = jest.fn(); + + const wrapper = mount( + + ); + wrapper.update(); + + await waitFor(() => { + expect(wrapper).toMatchSnapshot(); + }); + }); + it('renders panel table container', async () => { const loading = false; const fetchCustomPanels = jest.fn(); const customPanelData: CustomPanelListType[] = panelsData.panels; const createCustomPanel = jest.fn(); const setBreadcrumbs = jest.fn(); - const parentBreadcrumb = [ - { text: 'Operational panels', href: '#/operational_panels/' }, - { text: 'Observability', href: 'observability#/' }, - ]; + const parentBreadcrumb = panelBreadCrumbs; const renameCustomPanel = jest.fn(); const cloneCustomPanel = jest.fn(); const deleteCustomPanelList = jest.fn(); diff --git a/dashboards-observability/public/components/custom_panels/__tests__/custom_panel_view.test.tsx b/dashboards-observability/public/components/custom_panels/__tests__/custom_panel_view.test.tsx new file mode 100644 index 000000000..e183adfa6 --- /dev/null +++ b/dashboards-observability/public/components/custom_panels/__tests__/custom_panel_view.test.tsx @@ -0,0 +1,103 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { configure, mount } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +import React from 'react'; +import { CustomPanelView } from '../custom_panel_view'; +import { waitFor } from '@testing-library/react'; +import { + panelBreadCrumbs, + sampleEmptyPanel, + samplePanel, + samplePPLResponse, + sampleSavedVisualization, +} from '../../../../test/panels_constants'; +import httpClientMock from '../../../../test/__mocks__/httpClientMock'; +import PPLService from '../../../../public/services/requests/ppl'; +import { coreStartMock } from '../../../../test/__mocks__/coreMocks'; +import { HttpResponse } from '../../../../../../src/core/public'; + +describe('Panels View Component', () => { + configure({ adapter: new Adapter() }); + + it('renders panel view container without visualizations', async () => { + httpClientMock.get = jest.fn(() => + Promise.resolve((sampleEmptyPanel as unknown) as HttpResponse) + ); + const panelId = 'L8Sx53wBDp0rvEg3yoLb'; + const http = httpClientMock; + const pplService = new PPLService(httpClientMock); + const core = coreStartMock; + const parentBreadcrumb = panelBreadCrumbs; + const renameCustomPanel = jest.fn(); + const cloneCustomPanel = jest.fn(); + const deleteCustomPanel = jest.fn(); + const setToast = jest.fn(); + + const wrapper = mount( + + ); + wrapper.update(); + + await waitFor(() => { + expect(wrapper).toMatchSnapshot(); + }); + }); + + it('renders panel view container with visualizations', async () => { + let counter = 0; + // Mocks two http get requests first for fetch panel + // Others for fetching visualizations in panel + httpClientMock.get = jest.fn(() => { + if (counter === 0) { + counter += 1; + return Promise.resolve((samplePanel as unknown) as HttpResponse); + } else return Promise.resolve((sampleSavedVisualization as unknown) as HttpResponse); + }); + + httpClientMock.post = jest.fn(() => + Promise.resolve((samplePPLResponse as unknown) as HttpResponse) + ); + const panelId = 'L8Sx53wBDp0rvEg3yoLb'; + const http = httpClientMock; + const pplService = new PPLService(httpClientMock); + const core = coreStartMock; + const parentBreadcrumb = panelBreadCrumbs; + const renameCustomPanel = jest.fn(); + const cloneCustomPanel = jest.fn(); + const deleteCustomPanel = jest.fn(); + const setToast = jest.fn(); + + const wrapper = mount( + + ); + wrapper.update(); + + await waitFor(() => { + expect(wrapper).toMatchSnapshot(); + }); + }); +}); diff --git a/dashboards-observability/public/components/custom_panels/custom_panel_view.tsx b/dashboards-observability/public/components/custom_panels/custom_panel_view.tsx index dddd0a820..4acb3823d 100644 --- a/dashboards-observability/public/components/custom_panels/custom_panel_view.tsx +++ b/dashboards-observability/public/components/custom_panels/custom_panel_view.tsx @@ -197,7 +197,7 @@ export const CustomPanelView = ({ }; const onKeyPress = (e: React.KeyboardEvent) => { - if (e.key === 'Enter') onRefreshFilters(); + if (e.key === 'Enter') onRefreshFilters(start, end); }; const onDatePickerChange = (props: OnTimeChangeProps) => { @@ -209,7 +209,7 @@ export const CustomPanelView = ({ setStart, setEnd ); - onRefreshFilters(); + onRefreshFilters(props.start, props.end); }; const onDelete = async () => { @@ -328,8 +328,8 @@ export const CustomPanelView = ({ } }; - const onRefreshFilters = () => { - if (!isDateValid(convertDateTime(start), convertDateTime(end, false), setToast)) { + const onRefreshFilters = (startTime: ShortDate, endTime: ShortDate) => { + if (!isDateValid(convertDateTime(startTime), convertDateTime(endTime, false), setToast)) { return; } @@ -341,8 +341,8 @@ export const CustomPanelView = ({ panelId: panelId, query: pplFilterValue, language: 'ppl', - to: end, - from: start, + to: endTime, + from: startTime, }; http @@ -466,11 +466,6 @@ export const CustomPanelView = ({ fetchCustomPanel(); }, [panelId]); - // Check Validity of Time - useEffect(() => { - isDateValid(convertDateTime(start), convertDateTime(end, false), setToast); - }, [start, end]); - // Toggle input type (disabled or not disabled) // Disabled when there no visualizations in panels or when the panel is in edit mode useEffect(() => { @@ -593,29 +588,28 @@ export const CustomPanelView = ({ - {panelVisualizations.length > 0 ? ( - - ) : ( + {panelVisualizations.length === 0 && ( )} + diff --git a/dashboards-observability/public/components/custom_panels/helpers/utils.tsx b/dashboards-observability/public/components/custom_panels/helpers/utils.tsx index c401c332e..be8615968 100644 --- a/dashboards-observability/public/components/custom_panels/helpers/utils.tsx +++ b/dashboards-observability/public/components/custom_panels/helpers/utils.tsx @@ -320,4 +320,3 @@ export const displayVisualization = (data: any, type: string, editMode?: boolean } return vizComponent; }; - diff --git a/dashboards-observability/public/components/custom_panels/panel_modules/panel_grid/panel_grid.tsx b/dashboards-observability/public/components/custom_panels/panel_modules/panel_grid/panel_grid.tsx index 2246cb820..038f16065 100644 --- a/dashboards-observability/public/components/custom_panels/panel_modules/panel_grid/panel_grid.tsx +++ b/dashboards-observability/public/components/custom_panels/panel_modules/panel_grid/panel_grid.tsx @@ -150,16 +150,18 @@ export const PanelGrid = ({ if (editMode) { reloadLayout(); loadVizComponents(); - } else { - if (editActionType === 'save') { - const visualizationParams = postEditLayout.map((layout) => - _.omit(layout, ['static', 'moved']) - ); - saveVisualizationLayouts(panelId, visualizationParams); - } } }, [editMode]); + useEffect(() => { + if (editActionType === 'save') { + const visualizationParams = postEditLayout.map((layout) => + _.omit(layout, ['static', 'moved']) + ); + saveVisualizationLayouts(panelId, visualizationParams); + } + }, [editActionType]); + // Update layout whenever visualizations are updated useEffect(() => { reloadLayout(); @@ -173,6 +175,10 @@ export const PanelGrid = ({ }, 300); }, [isLocked]); + useEffect(() => { + loadVizComponents(); + }, [onRefresh]); + useEffect(() => { loadVizComponents(); }, []); diff --git a/dashboards-observability/public/components/custom_panels/panel_modules/visualization_container/__tests__/__snapshots__/visualization_container.test.tsx.snap b/dashboards-observability/public/components/custom_panels/panel_modules/visualization_container/__tests__/__snapshots__/visualization_container.test.tsx.snap index be7534ea4..100d6bc41 100644 --- a/dashboards-observability/public/components/custom_panels/panel_modules/visualization_container/__tests__/__snapshots__/visualization_container.test.tsx.snap +++ b/dashboards-observability/public/components/custom_panels/panel_modules/visualization_container/__tests__/__snapshots__/visualization_container.test.tsx.snap @@ -80,10 +80,11 @@ exports[`Visualization Container Component renders add visualization container 1
- + {disablePopover ? (