From 7b5c4949f1d1b1dd254ab3239366a29ae03629c8 Mon Sep 17 00:00:00 2001 From: Peter Fitzgibbons Date: Wed, 19 Apr 2023 10:56:25 -0700 Subject: [PATCH 01/12] Fixed Panels Table - Delete Signed-off-by: Peter Fitzgibbons --- .../custom_panels/custom_panel_table.tsx | 20 ++++-- public/components/custom_panels/home.tsx | 64 +------------------ .../custom_panels/redux/panel_slice.ts | 31 ++++++--- 3 files changed, 37 insertions(+), 78 deletions(-) diff --git a/public/components/custom_panels/custom_panel_table.tsx b/public/components/custom_panels/custom_panel_table.tsx index c0914e0b74..2c336aafab 100644 --- a/public/components/custom_panels/custom_panel_table.tsx +++ b/public/components/custom_panels/custom_panel_table.tsx @@ -42,12 +42,13 @@ import { } from '../../../common/constants/custom_panels'; import { UI_DATE_FORMAT } from '../../../common/constants/shared'; import { getCustomModal } from './helpers/modal_containers'; -import { CustomPanelListType } from '../../../common/types/custom_panels'; +import { CustomPanelListType, CustomPanelType } from '../../../common/types/custom_panels'; import { getSampleDataModal } from '../common/helpers/add_sample_modal'; import { pageStyles } from '../../../common/constants/shared'; import { DeleteModal } from '../common/helpers/delete_modal'; import { createPanel, + deletePanels, fetchPanels, newPanelTemplate, renameCustomPanel, @@ -86,11 +87,11 @@ export const CustomPanelTable = ({ deleteCustomPanelList, addSamplePanels, }: Props) => { - const customPanels = useSelector(selectPanelList); + const customPanels = useSelector(selectPanelList); const [isModalVisible, setIsModalVisible] = useState(false); // Modal Toggle const [modalLayout, setModalLayout] = useState(); // Modal Layout const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false); - const [selectedCustomPanels, setselectedCustomPanels] = useState([]); + const [selectedCustomPanels, setselectedCustomPanels] = useState([]); const [searchQuery, setSearchQuery] = useState(''); const location = useLocation(); const history = useHistory(); @@ -144,8 +145,17 @@ export const CustomPanelTable = ({ const toastMessage = `Observability Dashboards ${ selectedCustomPanels.length > 1 ? 's' : ' ' + selectedCustomPanels[0].title } successfully deleted!`; - const PanelList = selectedCustomPanels.map((panel) => panel.id); - deleteCustomPanelList(PanelList, toastMessage); + + try { + dispatch(deletePanels(selectedCustomPanels)); + } catch (err) { + // setToast( + // 'Error deleting Operational Panels, please make sure you have the correct permission.', + // 'danger' + // ); + console.error(err.body?.message || err); + } + closeModal(); }; diff --git a/public/components/custom_panels/home.tsx b/public/components/custom_panels/home.tsx index 6c86318e26..f0a501b141 100644 --- a/public/components/custom_panels/home.tsx +++ b/public/components/custom_panels/home.tsx @@ -28,7 +28,7 @@ import PPLService from '../../services/requests/ppl'; import { CustomPanelTable } from './custom_panel_table'; import { CustomPanelView } from './custom_panel_view'; import { CustomPanelViewSO } from './custom_panel_view_so'; -import { deletePanel, fetchPanels, uuidRx } from './redux/panel_slice'; +import { fetchPanels, uuidRx } from './redux/panel_slice'; // import { ObjectFetcher } from '../common/objectFetcher'; @@ -91,65 +91,6 @@ export const Home = ({ window.location.assign(`${observabilityLogsID}#/explorer/${savedVisualizationId}`); }; - const deletePanelSO = (customPanelIdList: string[]) => { - const soPanelIds = customPanelIdList.filter((id) => id.match(uuidRx)); - return Promise.all( - soPanelIds.map((id) => - coreRefs.savedObjectsClient?.delete(CUSTOM_PANELS_SAVED_OBJECT_TYPE, id) - ) - ); - }; - - const deletePanels = (customPanelIdList: string[]) => { - const panelIds = customPanelIdList.filter((id) => !id.match(uuidRx)); - const concatList = panelIds.toString(); - return http.delete(`${CUSTOM_PANELS_API_PREFIX}/panelList/` + concatList); - }; - - // Deletes multiple existing Operational Panels - const deleteCustomPanelList = (customPanelIdList: string[], toastMessage: string) => { - Promise.all([deletePanelSO(customPanelIdList), deletePanels(customPanelIdList)]) - .then((res) => { - // setcustomPanelData((prevCustomPanelData) => { - // return prevCustomPanelData.filter( - // (customPanel) => !customPanelIdList.includes(customPanel.id) - // ); - // }); - // setToast(toastMessage); - }) - .catch((err) => { - setToast( - 'Error deleting Operational Panels, please make sure you have the correct permission.', - 'danger' - ); - console.error(err.body.message); - }); - }; - - // Deletes an existing Observability Dashboard - const deleteCustomPanel = async (customPanelId: string, customPanelName: string) => { - return http - .delete(`${CUSTOM_PANELS_API_PREFIX}/panels/` + customPanelId) - .then((res) => { - dispatch(fetchPanels()); - setToast(`Observability Dashboard "${customPanelName}" successfully deleted!`); - return res; - }) - .catch((err) => { - setToast( - 'Error deleting Observability Dashboard, please make sure you have the correct permission.', - 'danger' - ); - console.error(err.body.message); - }); - }; - - // Deletes an existing SO Observability Dashboard - const deleteCustomPanelSO = async (customPanelId: string, customPanelName: string) => { - dispatch(deletePanel(customPanelId)); - // TODO: toast here - }; - const addSamplePanels = async () => { try { setLoading(true); @@ -222,7 +163,6 @@ export const Home = ({ loading={loading} setBreadcrumbs={chrome.setBreadcrumbs} parentBreadcrumbs={customPanelBreadCrumbs} - deleteCustomPanelList={deleteCustomPanelList} addSamplePanels={addSamplePanels} /> ); @@ -238,7 +178,6 @@ export const Home = ({ panelId={props.match.params.id} chrome={chrome} parentBreadcrumbs={customPanelBreadCrumbs} - deleteCustomPanel={deleteCustomPanel} setToast={setToast} onEditClick={onEditClick} page="operationalPanels" @@ -253,7 +192,6 @@ export const Home = ({ chrome={chrome} parentBreadcrumbs={customPanelBreadCrumbs} // renameCustomPanel={renameCustomPanel} - deleteCustomPanel={deleteCustomPanel} setToast={setToast} onEditClick={onEditClick} startTime={start} diff --git a/public/components/custom_panels/redux/panel_slice.ts b/public/components/custom_panels/redux/panel_slice.ts index a3028a4dfd..120a5de255 100644 --- a/public/components/custom_panels/redux/panel_slice.ts +++ b/public/components/custom_panels/redux/panel_slice.ts @@ -25,7 +25,7 @@ interface InitialState { panelList: CustomPanelType[]; } -export const newPanelTemplate = (newName) => ({ +export const newPanelTemplate = (newName): PanelType => ({ title: newName, dateCreated: new Date().getTime(), dateModified: new Date().getTime(), @@ -66,18 +66,13 @@ export const selectPanel = createSelector( (panel) => normalizedPanel(panel) ); -const normalizedPanel = (panel): PanelType => ({ +const normalizedPanel = (panel: CustomPanelType): CustomPanelType => ({ ...newPanelTemplate(''), ...panel, }); export const selectPanelList = (rootState): CustomPanelType[] => rootState.customPanel.panelList; -// export const selectPanelList = createSelector( -// rootState => { console.log("selectPanelList", { rootState }); return rootState.customPanel.panelList }, -// panelList => panelList.map(p => p as CustomPanelListType) -// ); - /* ** ASYNC DISPATCH FUNCTIONS */ @@ -178,9 +173,25 @@ export const replaceVizInPanel = (oldPanel, oldVizId, vizId) => async (dispatch, } }; -export const deletePanel = (id) => async (dispatch, getState) => { - await savedObjectPanelsClient.delete(id); - const panelList: CustomPanelType[] = getState().panelList.filter((p) => p.id !== id); +const deletePanelSO = (customPanelIdList: string[]) => { + const soPanelIds = customPanelIdList.filter((id) => id.match(uuidRx)); + console.log('deletePanelSO', soPanelIds); + return Promise.all(soPanelIds.map((id) => savedObjectPanelsClient.delete(id))); +}; + +const deleteLegacyPanels = (customPanelIdList: string[]) => { + const panelIds = customPanelIdList.filter((id) => !id.match(uuidRx)); + const concatList = panelIds.toString(); + return coreRefs.http!.delete(`${CUSTOM_PANELS_API_PREFIX}/panelList/` + concatList); +}; + +export const deletePanels = (panelsToDelete: CustomPanelType[]) => async (dispatch, getState) => { + const ids = panelsToDelete.map((p) => p.id); + await Promise.all([deleteLegacyPanels(ids), deletePanelSO(ids)]); + + const panelList: CustomPanelType[] = getState().customPanel.panelList.filter( + (p) => !ids.includes(p.id) + ); dispatch(setPanelList(panelList)); }; From 4a4c749f7941bc7737d0a4cba490a7d70f0f020f Mon Sep 17 00:00:00 2001 From: Peter Fitzgibbons Date: Wed, 19 Apr 2023 13:36:46 -0700 Subject: [PATCH 02/12] Fixes * Panel View (legacy) - Duplicate - Rename Signed-off-by: Peter Fitzgibbons --- .../custom_panels/custom_panel_table.tsx | 33 +++++++++++++++---- .../custom_panels/custom_panel_view_so.tsx | 31 ++++++++++++++--- .../custom_panels/redux/panel_slice.ts | 9 ++--- 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/public/components/custom_panels/custom_panel_table.tsx b/public/components/custom_panels/custom_panel_table.tsx index 2c336aafab..a6107953b8 100644 --- a/public/components/custom_panels/custom_panel_table.tsx +++ b/public/components/custom_panels/custom_panel_table.tsx @@ -33,11 +33,12 @@ import React, { ReactElement, useEffect, useState } from 'react'; import moment from 'moment'; import _ from 'lodash'; import { useHistory, useLocation } from 'react-router-dom'; -import { coreRefs } from 'public/framework/core_refs'; import { useDispatch, useSelector } from 'react-redux'; +import { coreRefs } from '../../framework/core_refs'; import { ChromeBreadcrumb } from '../../../../../src/core/public'; import { CREATE_PANEL_MESSAGE, + CUSTOM_PANELS_API_PREFIX, CUSTOM_PANELS_DOCUMENTATION_URL, } from '../../../common/constants/custom_panels'; import { UI_DATE_FORMAT } from '../../../common/constants/shared'; @@ -47,9 +48,11 @@ import { getSampleDataModal } from '../common/helpers/add_sample_modal'; import { pageStyles } from '../../../common/constants/shared'; import { DeleteModal } from '../common/helpers/delete_modal'; import { + clonePanel, createPanel, deletePanels, fetchPanels, + isUuid, newPanelTemplate, renameCustomPanel, selectPanelList, @@ -134,10 +137,26 @@ export const CustomPanelTable = ({ }; const onClone = async (newName: string) => { - const sourcePanel = selectedCustomPanels[0]; - const { id, ...newPanel } = { ...sourcePanel, title: sourcePanel.title + ' (copy)' }; + let sourcePanel = selectedCustomPanels[0]; + try { + if (!isUuid(sourcePanel.id)) { + // Observability Panel API returns partial record, so for duplication + // we will retrieve the entire record and allow new process to continue. + const legacyFetchResult = await coreRefs.http!.get( + `${CUSTOM_PANELS_API_PREFIX}/panels/${sourcePanel.id}` + ); + sourcePanel = legacyFetchResult.operationalPanel; + } - dispatch(createPanel(newPanel)); + const { id, ...newPanel } = { + ...sourcePanel, + title: newName, + }; + + dispatch(createPanel(newPanel)); + } catch (err) { + console.log(err); + } closeModal(); }; @@ -194,7 +213,7 @@ export const CustomPanelTable = ({ showModal(); }; - const clonePanel = () => { + const clonePanelModal = () => { setModalLayout( getCustomModal( onClone, @@ -203,7 +222,7 @@ export const CustomPanelTable = ({ 'Duplicate Dashboard', 'Cancel', 'Duplicate', - selectedCustomPanels[0].title + ' (copy)', + selectedCustomPanels[0].title + ' (copy)x', CREATE_PANEL_MESSAGE ) ); @@ -264,7 +283,7 @@ export const CustomPanelTable = ({ disabled={customPanels.length === 0 || selectedCustomPanels.length !== 1} onClick={() => { setIsActionsPopoverOpen(false); - clonePanel(); + clonePanelModal(); }} > Duplicate diff --git a/public/components/custom_panels/custom_panel_view_so.tsx b/public/components/custom_panels/custom_panel_view_so.tsx index 19eb6cde4d..fa358c6f46 100644 --- a/public/components/custom_panels/custom_panel_view_so.tsx +++ b/public/components/custom_panels/custom_panel_view_so.tsx @@ -71,6 +71,7 @@ import { addVisualizationPanel } from './helpers/add_visualization_helper'; import { clonePanel, createPanel, + deletePanels, fetchPanel, newPanelTemplate, selectPanel, @@ -114,7 +115,6 @@ interface CustomPanelViewProps { coreSavedObjects: CoreStart['savedObjects']; chrome: CoreStart['chrome']; parentBreadcrumbs: EuiBreadcrumb[]; - deleteCustomPanel: (customPanelId: string, customPanelName: string) => Promise; cloneCustomPanel: (clonedCustomPanelName: string, clonedCustomPanelId: string) => Promise; setToast: ( title: string, @@ -140,7 +140,6 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { parentBreadcrumbs, childBreadcrumbs, updateAvailabilityVizId, - deleteCustomPanel, cloneCustomPanel, setToast, onEditClick, @@ -210,11 +209,20 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { }; const onDelete = async () => { - deleteCustomPanel(panelId, panel?.title).then((res) => { + const toastMessage = `Observability Dashboard ${panel.title} successfully deleted!"`; + try { + await dispatch(deletePanels([panel])); + setTimeout(() => { window.location.assign(`${last(parentBreadcrumbs)!.href}`); }, 1000); - }); + } catch (err) { + setToast( + 'Error deleting Operational Panels, please make sure you have the correct permission.', + 'danger' + ); + console.error(err.body?.message || err); + } closeModal(); }; @@ -230,6 +238,21 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { showModal(); }; + const onRename = async (newCustomPanelName: string) => { + const newPanel = { ...panel, title: newCustomPanelName }; + try { + dispatch(updatePanel(newPanel)); + setToast(`Operational Panel successfully renamed into "${newCustomPanelName}"`); + } catch (err) { + setToast( + 'Error renaming Operational Panel, please make sure you have the correct permission.', + 'danger' + ); + console.error(err.body.message); + } + closeModal(); + }; + const renamePanel = () => { setModalLayout( getCustomModal( diff --git a/public/components/custom_panels/redux/panel_slice.ts b/public/components/custom_panels/redux/panel_slice.ts index 120a5de255..339241d1ae 100644 --- a/public/components/custom_panels/redux/panel_slice.ts +++ b/public/components/custom_panels/redux/panel_slice.ts @@ -126,7 +126,7 @@ const updateSavedObjectPanel = (panel: CustomPanelType) => savedObjectPanelsClie export const uuidRx = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/; -const isUuid = (id) => !!id.match(uuidRx); +export const isUuid = (id) => !!id.match(uuidRx); export const updatePanel = (panel: CustomPanelType) => async (dispatch, getState) => { try { @@ -174,13 +174,14 @@ export const replaceVizInPanel = (oldPanel, oldVizId, vizId) => async (dispatch, }; const deletePanelSO = (customPanelIdList: string[]) => { - const soPanelIds = customPanelIdList.filter((id) => id.match(uuidRx)); - console.log('deletePanelSO', soPanelIds); + const soPanelIds = customPanelIdList.filter((id) => isUuid(id)); return Promise.all(soPanelIds.map((id) => savedObjectPanelsClient.delete(id))); }; const deleteLegacyPanels = (customPanelIdList: string[]) => { - const panelIds = customPanelIdList.filter((id) => !id.match(uuidRx)); + const panelIds = customPanelIdList.filter((id) => !isUuid(id)); + if (panelIds.length === 0) return; + const concatList = panelIds.toString(); return coreRefs.http!.delete(`${CUSTOM_PANELS_API_PREFIX}/panelList/` + concatList); }; From f57c9e33872cfff4a9e0da0d2147371c3f19afe0 Mon Sep 17 00:00:00 2001 From: Peter Fitzgibbons Date: Wed, 19 Apr 2023 13:52:40 -0700 Subject: [PATCH 03/12] Fixes * Panel View (legacy) - Duplicate - Rename Signed-off-by: Peter Fitzgibbons --- .../custom_panels/custom_panel_view.tsx | 95 +++++++++++++++---- 1 file changed, 77 insertions(+), 18 deletions(-) diff --git a/public/components/custom_panels/custom_panel_view.tsx b/public/components/custom_panels/custom_panel_view.tsx index 99075358bf..2384d82efc 100644 --- a/public/components/custom_panels/custom_panel_view.tsx +++ b/public/components/custom_panels/custom_panel_view.tsx @@ -30,6 +30,7 @@ import React, { useEffect, useState } from 'react'; import { DurationRange } from '@elastic/eui/src/components/date_picker/types'; import moment from 'moment'; import _ from 'lodash'; +import { useDispatch } from 'react-redux'; import DSLService from '../../services/requests/dsl'; import { CoreStart } from '../../../../../src/core/public'; import { EmptyPanelView } from './panel_modules/empty_panel'; @@ -53,6 +54,7 @@ import { onTimeChange, isPPLFilterValid, fetchVisualizationById, + isNameValid, } from './helpers/utils'; import { UI_DATE_FORMAT } from '../../../common/constants/shared'; import { VisaulizationFlyout } from './panel_modules/visualization_flyout'; @@ -67,6 +69,7 @@ import { import { AddVisualizationPopover } from './helpers/add_visualization_popover'; import { DeleteModal } from '../common/helpers/delete_modal'; import { coreRefs } from '../../framework/core_refs'; +import { clonePanel } from './redux/panel_slice'; /* * "CustomPanelsView" module used to render an Observability Dashboard @@ -79,7 +82,6 @@ import { coreRefs } from '../../framework/core_refs'; * dslService: dsl requestor service * chrome: chrome core service * parentBreadcrumb: parent breadcrumb - * renameCustomPanel: Rename function for the panel * deleteCustomPanel: Delete function for the panel * cloneCustomPanel: Clone function for the panel * setToast: create Toast function @@ -101,8 +103,6 @@ interface CustomPanelViewProps { dslService: DSLService; chrome: CoreStart['chrome']; parentBreadcrumbs: EuiBreadcrumb[]; - renameCustomPanel: (editedCustomPanelName: string, editedCustomPanelId: string) => Promise; - deleteCustomPanel: (customPanelId: string, customPanelName: string) => Promise; cloneCustomPanel: (clonedCustomPanelName: string, clonedCustomPanelId: string) => Promise; setToast: ( title: string, @@ -137,8 +137,6 @@ export const CustomPanelView = (props: CustomPanelViewProps) => { setStartTime, setEndTime, updateAvailabilityVizId, - renameCustomPanel, - deleteCustomPanel, cloneCustomPanel, setToast, onEditClick, @@ -169,6 +167,8 @@ export const CustomPanelView = (props: CustomPanelViewProps) => { const appPanel = page === 'app'; + const dispatch = useDispatch(); + const closeHelpFlyout = () => { setAddVizDisabled(false); setHelpIsFlyoutVisible(false); @@ -200,6 +200,59 @@ export const CustomPanelView = (props: CustomPanelViewProps) => { }); }; + // Renames an existing CustomPanel + const renameCustomPanel = (editedCustomPanelName: string, editedCustomPanelId: string) => { + if (!isNameValid(editedCustomPanelName)) { + setToast('Invalid Custom Panel name', 'danger'); + return Promise.reject(); + } + const renamePanelObject = { + panelId: editedCustomPanelId, + panelName: editedCustomPanelName, + }; + + return http + .post(`${CUSTOM_PANELS_API_PREFIX}/panels/rename`, { + body: JSON.stringify(renamePanelObject), + }) + .then((res) => { + setOpenPanelName(editedCustomPanelName); + // setOpenPanelName((prevCustomPanelData) => { + // const newCustomPanelData = [...prevCustomPanelData]; + // const renamedCustomPanel = newCustomPanelData.find( + // (customPanel) => customPanel.id === editedCustomPanelId + // ); + // if (renamedCustomPanel) renamedCustomPanel.name = editedCustomPanelName; + // return newCustomPanelData; + // }); + setToast(`Operational Panel successfully renamed into "${editedCustomPanelName}"`); + }) + .catch((err) => { + setToast( + 'Error renaming Operational Panel, please make sure you have the correct permission.', + 'danger' + ); + console.error(err.body.message); + }); + }; + + // Deletes an existing Operational Panel + const deleteCustomPanel = (customPanelId: string, customPanelName: string) => { + return coreRefs + .http!.delete(`${CUSTOM_PANELS_API_PREFIX}/panels/` + customPanelId) + .then((res) => { + setToast(`Operational Panel "${customPanelName}" successfully deleted!`); + return res; + }) + .catch((err) => { + setToast( + 'Error deleting Operational Panel, please make sure you have the correct permission.', + 'danger' + ); + console.error(err.body.message); + }); + }; + const handleQueryChange = (newQuery: string) => { setPPLFilterValue(newQuery); }; @@ -265,22 +318,28 @@ export const CustomPanelView = (props: CustomPanelViewProps) => { }; const onClone = async (newCustomPanelName: string) => { - const newPanel = { - ...panel, - title: newCustomPanelName, - dateCreated: new Date().getTime(), - dateModified: new Date().getTime(), - } as PanelType; - const newSOPanel = await coreRefs.savedObjectsClient!.create( - CUSTOM_PANELS_SAVED_OBJECT_TYPE, - newPanel - ); + try { + await dispatch(clonePanel(panel, newCustomPanelName)); + } catch (err) { + setToast('Error while attempting to Duplicate this Dashboard.', 'danger'); + } - window.location.assign(`${last(parentBreadcrumbs)!.href}${newSOPanel.id}`); + // const newPanel = { + // ...panel, + // title: newCustomPanelName, + // dateCreated: new Date().getTime(), + // dateModified: new Date().getTime(), + // } as PanelType; + // const newSOPanel = await coreRefs.savedObjectsClient!.create( + // CUSTOM_PANELS_SAVED_OBJECT_TYPE, + // newPanel + // ); + // + // window.location.assign(`${last(parentBreadcrumbs)!.href}${newSOPanel.id}`); closeModal(); }; - const clonePanel = () => { + const clonePanelModal = () => { setModalLayout( getCustomModal( onClone, @@ -518,7 +577,7 @@ export const CustomPanelView = (props: CustomPanelViewProps) => { 'data-test-subj': 'duplicatePanelContextMenuItem', onClick: () => { setPanelsMenuPopover(false); - clonePanel(); + clonePanelModal(); }, }, { From b3f4a25ac3e041ea159b1f432e216539c836fb8a Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Wed, 19 Apr 2023 15:14:27 -0700 Subject: [PATCH 04/12] Fix redirection to legacy event_analytics URL from dashboards (#399) (#403) Signed-off-by: Joshua Li (cherry picked from commit 05ef1806c44c91ff1099f6f23ec2176005643873) Co-authored-by: Joshua Li --- .../helpers/add_visualization_popover.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/public/components/custom_panels/helpers/add_visualization_popover.tsx b/public/components/custom_panels/helpers/add_visualization_popover.tsx index 4dac54ab30..d36fcd149b 100644 --- a/public/components/custom_panels/helpers/add_visualization_popover.tsx +++ b/public/components/custom_panels/helpers/add_visualization_popover.tsx @@ -5,6 +5,12 @@ import { EuiButton, EuiContextMenu, EuiPopover } from '@elastic/eui'; import React, { useState } from 'react'; +import { + CREATE_TAB_PARAM, + CREATE_TAB_PARAM_KEY, + TAB_CHART_ID, +} from '../../../../common/constants/explorer'; +import { observabilityLogsID } from '../../../../common/constants/shared'; interface AddVisualizationPopoverProps { showFlyout: (isReplacement?: boolean, replaceVizId?: string) => void; @@ -27,7 +33,9 @@ export const AddVisualizationPopover = ({ const advancedVisualization = () => { closeVizPopover(); - window.location.assign('#/event_analytics/explorer'); + window.location.assign( + `${observabilityLogsID}#/explorer?${CREATE_TAB_PARAM_KEY}=${CREATE_TAB_PARAM[TAB_CHART_ID]}` + ); }; const getVizContextPanels = () => { From d6bd1074c436965d3910289779924af30ef277d5 Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Wed, 19 Apr 2023 15:14:34 -0700 Subject: [PATCH 05/12] Adjust metrics top menu layout to avoid overflow (#398) (#402) Signed-off-by: Joshua Li (cherry picked from commit 6309e635beb5b3463a2429e6b779155330895897) Co-authored-by: Joshua Li --- .../__snapshots__/searchbar.test.tsx.snap | 8 +- .../components/metrics/sidebar/search_bar.tsx | 2 +- .../__snapshots__/top_menu.test.tsx.snap | 5709 ++++++++--------- .../components/metrics/top_menu/top_menu.scss | 16 +- .../components/metrics/top_menu/top_menu.tsx | 201 +- 5 files changed, 2945 insertions(+), 2991 deletions(-) diff --git a/public/components/metrics/sidebar/__tests__/__snapshots__/searchbar.test.tsx.snap b/public/components/metrics/sidebar/__tests__/__snapshots__/searchbar.test.tsx.snap index c4fcb663bf..9232c58180 100644 --- a/public/components/metrics/sidebar/__tests__/__snapshots__/searchbar.test.tsx.snap +++ b/public/components/metrics/sidebar/__tests__/__snapshots__/searchbar.test.tsx.snap @@ -15,7 +15,9 @@ exports[`Search Bar Component Search Side Bar Component with available metrics 1 -
+
-
+
{ }; return ( -
+
- -
+
- -
- -
- -
- -
- - -
- -
- - - -
-
- - - - -
- - - - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +
- +
- -
- - } - aria-label="resolutionField" - className="resolutionSelectText" - disabled={true} - isInvalid={false} - onChange={[Function]} - prepend="Span Interval" - value={1} + - - } - fullWidth={false} - prepend="Span Interval" +
-
- - - -
- - - - -
-
- - + +
@@ -496,635 +203,261 @@ exports[`Metrics Top Menu Component renders Top Menu Component when disabled in
- -
-
- + + +
+
-
- - -
- + +
+ +
+
+ +
+
+ + } + aria-label="resolutionField" + className="resolutionSelectText" + disabled={true} + isInvalid={false} + onChange={[Function]} + prepend="Span Interval" + value={1} + > + + } + fullWidth={false} + prepend="Span Interval" + > +
+ + + +
+ + + + +
+ -
- -
- - } - > -
- - - - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="m" - > -
-
- - - -
-
-
-
-
- } - iconType={false} - isCustom={true} - startDateControl={
} - > -
- -
- - -
-
- -
- - -
- + + + +
- - - Save - - - - - + +
+
+
-
- + +
- +
+
+
+
+
+ +
+ + +
+ +
+ + } + > +
+ + + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+
+ + + +
+
+
+
+
+ } + iconType={false} + isCustom={true} + startDateControl={
} + > +
+ +
+ + +
+
+ +
+ + +
+ + + } + delay="regular" + position="bottom" + > + + + + + + + + + +
+
+
+ + +
+ + +
+ + Save + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+
+ + + + + +
- +
- -
-
+ +
+ + +
+ - -
+
- +
- +
- -
+ - -
- + +
- -
-
- - + + + - -
-
- - -
- - - - - -
-
-
-
-
-
-
-
-
-
-
- -
- -
- -
- -
- - -
- -
- -
-
- - } - aria-label="resolutionField" - className="resolutionSelectText" - disabled={true} - isInvalid={false} - onChange={[Function]} - prepend="Span Interval" - value={1} - > - - } - fullWidth={false} - prepend="Span Interval" - > -
- - - -
- - - - -
- - -
-
- - - - -
- - - @@ -1808,635 +1502,261 @@ exports[`Metrics Top Menu Component renders Top Menu Component when disabled wit
- -
-
- + + +
+
-
-
- -
- + +
+ +
+ + +
+
+ + } + aria-label="resolutionField" + className="resolutionSelectText" + disabled={true} + isInvalid={false} + onChange={[Function]} + prepend="Span Interval" + value={1} + > + + } + fullWidth={false} + prepend="Span Interval" + > +
+ + + +
+ + + + +
+ -
- -
- - } - > -
- - - - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="m" - > -
-
- - - -
-
-
-
-
- } - iconType={false} - isCustom={true} - startDateControl={
} - > -
- -
- - -
-
- -
- - -
- + + + +
- - - Save - - - - - + +
+
+
-
- + +
- +
+
+
+
+
+ +
+ + +
+ +
+ + } + > +
+ + + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+
+ + + +
+
+
+
+
+ } + iconType={false} + isCustom={true} + startDateControl={
} + > +
+ +
+ + +
+
+ +
+ + +
+ + + } + delay="regular" + position="bottom" + > + + + + + + + + + +
+
+
+ + +
+ + +
+ + Save + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+
+ + + + + +
- +
- -
-
+ +
+ + +
+ - -
+
- +
- +
- -
+ - -
- + +
- -
-
- - + + + - -
-
- - - - -
- - - - - -
-
-
-
-
-
-
-
-
-
-
- -
- -
- -
- -
- - -
- -
- -
-
- - } - aria-label="resolutionField" - className="resolutionSelectText" - disabled={false} - isInvalid={false} - onChange={[Function]} - prepend="Span Interval" - value={1} - > - - } - fullWidth={false} - prepend="Span Interval" - > -
- - - -
- - - - -
- - -
-
- - - - -
- -
- -
- - + + +
+
-
- - -
- + +
+ +
+
+ +
+
+ + } + aria-label="resolutionField" + className="resolutionSelectText" + disabled={false} + isInvalid={false} + onChange={[Function]} + prepend="Span Interval" + value={1} + > + + } + fullWidth={false} + prepend="Span Interval" + > +
+ + + +
+ + + + +
+ -
- -
- +
+ + + + +
-
- - - - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="m" - > -
-
- - - -
-
-
-
-
- } - iconType={false} - isCustom={true} - startDateControl={
} +
-
- -
- - + + + + +
+
+
+
+ + +
+ + +
+
+ + +
+ + +
+ +
+ + } + > +
+ -
+ + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" > - - - - - - - - + viewBox="0 0 16 16" + width={16} + xmlns="http://www.w3.org/2000/svg" + /> + + + + + + + +
+
+ + +
+ } + iconType={false} + isCustom={true} + startDateControl={
} + > +
+
- + +
+
+ +
- - -
- - -
+
+
+ - - Save - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="m" +
-
-
- - - - - -
-
- -
-
+ + + + + + + +
+
+
+ + +
+ + +
+ + Save + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+
+ + + + + +
- +
- -
-
+ +
+ + +
+ { let savedMetricIds = []; - let savedMetricsInPanels = []; + const savedMetricsInPanels = []; try { savedMetricIds = await Promise.all( @@ -220,92 +219,86 @@ export const TopMenu = ({ return ( <> - - - - - - - - - - - -
- setSpanValue(e.target.value)} - append={ - onResolutionChange(e)} - aria-label="resolutionSelect" - /> - } - disabled={IsTopPanelDisabled} - aria-label="resolutionField" + + + + + +
+ setSpanValue(e.target.value)} + append={ + onResolutionChange(e)} + aria-label="resolutionSelect" /> -
-
- - - - - setIsSavePanelOpen(false)} - > - - - - - setIsSavePanelOpen(false)} - data-test-subj="metrics__SaveCancel" - > - Cancel - - - - { - handleSavingObjects().then(() => setIsSavePanelOpen(false)); - }} - data-test-subj="metrics__SaveConfirm" - > - Save - - - - - - -
- - + } + disabled={IsTopPanelDisabled} + aria-label="resolutionField" + /> +
+
+ + + + + setIsSavePanelOpen(false)} + > + + + + + setIsSavePanelOpen(false)} + data-test-subj="metrics__SaveCancel" + > + Cancel + + + + { + handleSavingObjects().then(() => setIsSavePanelOpen(false)); + }} + data-test-subj="metrics__SaveConfirm" + > + Save + + + + + + +
+ {editMode ? ( <> From 148c1673cb0bc11c54eaf19548016775da1ccf12 Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Wed, 19 Apr 2023 19:00:26 -0700 Subject: [PATCH 06/12] Support duplicate visualization in dashboard (#400) (#405) Signed-off-by: Joshua Li (cherry picked from commit 3d74bacd8431ee6315dadc3b82463a2d958d1032) Co-authored-by: Joshua Li --- .../custom_panels/custom_panel_view_so.tsx | 53 ++++++++----------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/public/components/custom_panels/custom_panel_view_so.tsx b/public/components/custom_panels/custom_panel_view_so.tsx index fa358c6f46..68529f8013 100644 --- a/public/components/custom_panels/custom_panel_view_so.tsx +++ b/public/components/custom_panels/custom_panel_view_so.tsx @@ -5,7 +5,6 @@ /* // eslint-disable no-console */ /* eslint-disable react-hooks/exhaustive-deps */ -import { useCallback } from 'react'; import { EuiBreadcrumb, EuiButton, @@ -27,45 +26,32 @@ import { OnTimeChangeProps, ShortDate, } from '@elastic/eui'; -import { last } from 'lodash'; -import React, { useEffect, useState } from 'react'; import { DurationRange } from '@elastic/eui/src/components/date_picker/types'; +import { last } from 'lodash'; import moment from 'moment'; -import _ from 'lodash'; +import React, { useCallback, useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { useRef } from 'react'; -import DSLService from '../../services/requests/dsl'; -import { CoreStart, SimpleSavedObject } from '../../../../../src/core/public'; -import { EmptyPanelView } from './panel_modules/empty_panel'; -import { - CREATE_PANEL_MESSAGE, - CUSTOM_PANELS_API_PREFIX, - CUSTOM_PANELS_SAVED_OBJECT_TYPE, -} from '../../../common/constants/custom_panels'; -import { CustomPanelType, PanelType } from '../../../common/types/custom_panels'; -import { PanelGridSO } from './panel_modules/panel_grid/panel_grid_so'; - +import { CoreStart } from '../../../../../src/core/public'; +import { CREATE_PANEL_MESSAGE } from '../../../common/constants/custom_panels'; +import { UI_DATE_FORMAT } from '../../../common/constants/shared'; +import { CustomPanelType } from '../../../common/types/custom_panels'; +import { uiSettingsService } from '../../../common/utils'; +import { coreRefs } from '../../framework/core_refs'; +import { PPLReferenceFlyout } from '../common/helpers'; +import { DeleteModal } from '../common/helpers/delete_modal'; +import { Autocomplete } from '../common/search/autocomplete'; +import { onItemSelect, parseGetSuggestions } from '../common/search/autocomplete_logic'; +import { addVisualizationPanel } from './helpers/add_visualization_helper'; +import { AddVisualizationPopover } from './helpers/add_visualization_popover'; import { getCustomModal } from './helpers/modal_containers'; -import PPLService from '../../services/requests/ppl'; import { - isDateValid, convertDateTime, + isDateValid, isPPLFilterValid, - isNameValid, prependRecentlyUsedRange, } from './helpers/utils'; -import { UI_DATE_FORMAT } from '../../../common/constants/shared'; -import { VisaulizationFlyout } from './panel_modules/visualization_flyout'; -import { uiSettingsService } from '../../../common/utils'; -import { PPLReferenceFlyout } from '../common/helpers'; -import { Autocomplete } from '../common/search/autocomplete'; -import { - parseGetSuggestions, - onItemSelect, - parseForIndices, -} from '../common/search/autocomplete_logic'; -import { AddVisualizationPopover } from './helpers/add_visualization_popover'; -import { DeleteModal } from '../common/helpers/delete_modal'; +import { EmptyPanelView } from './panel_modules/empty_panel'; +import { PanelGridSO } from './panel_modules/panel_grid/panel_grid_so'; import { VisaulizationFlyoutSO } from './panel_modules/visualization_flyout/visualization_flyout_so'; import { addVisualizationPanel } from './helpers/add_visualization_helper'; import { @@ -398,6 +384,7 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { }; const cloneVisualization = (visualzationTitle: string, savedVisualizationId: string) => { + addVisualizationToCurrentPanel({ savedVisualizationId }); // http // .post(`${CUSTOM_PANELS_API_PREFIX}/visualizations`, { // body: JSON.stringify({ @@ -571,7 +558,9 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => { // Toggle input type (disabled or not disabled) // Disabled when there no visualizations in panels or when the panel is in edit mode useEffect(() => { - !loading && checkDisabledInputs(); + if (!loading) { + checkDisabledInputs(); + } }, [isEditing, loading]); // Build base query with all of the indices included in the current visualizations From 6832a76fe086a02199458c66fd390b33af701f80 Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Wed, 19 Apr 2023 19:35:17 -0700 Subject: [PATCH 07/12] fix panel visualization preview, new viz workflow (#401) (#404) Signed-off-by: Shenoy Pratik (cherry picked from commit 78347d9015848dea729c82fbbb4b1f69ebf5c686) Co-authored-by: Shenoy Pratik --- .../custom_panels/custom_panel_view_so.tsx | 2 ++ public/components/custom_panels/home.tsx | 2 ++ .../visualization_flyout_so.tsx | 23 ++++++++++++++++--- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/public/components/custom_panels/custom_panel_view_so.tsx b/public/components/custom_panels/custom_panel_view_so.tsx index 68529f8013..0b8cd491da 100644 --- a/public/components/custom_panels/custom_panel_view_so.tsx +++ b/public/components/custom_panels/custom_panel_view_so.tsx @@ -113,6 +113,8 @@ interface CustomPanelViewProps { appId?: string; updateAvailabilityVizId?: any; onAddClick?: any; + pplService: PPLService; + dslService: DSLService; } export const CustomPanelViewSO = (props: CustomPanelViewProps) => { diff --git a/public/components/custom_panels/home.tsx b/public/components/custom_panels/home.tsx index f0a501b141..c113307e1b 100644 --- a/public/components/custom_panels/home.tsx +++ b/public/components/custom_panels/home.tsx @@ -177,6 +177,8 @@ export const Home = ({ { - return http - .get(`${CUSTOM_PANELS_API_PREFIX}/visualizations`) + return SavedObjectsActions.getBulk({ + objectType: [SAVED_VISUALIZATION], + sortOrder: 'desc', + fromIndex: 0, + }) + .then((response) => ({ + visualizations: response.observabilityObjectList.map(parseSavedVisualizations), + })) .then((res) => { if (res.visualizations.length > 0) { setSavedVisualizations(res.visualizations); From e4593a11e20c4fa3be0236bcd22bc615628dad8c Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Thu, 20 Apr 2023 11:32:34 -0700 Subject: [PATCH 08/12] Fix saving multiple metrics to SOpanels (#407) (#408) * fix saving multiple metrics to SOpanels Signed-off-by: Shenoy Pratik * remove unused variable Signed-off-by: Shenoy Pratik --------- Signed-off-by: Shenoy Pratik (cherry picked from commit a4cb6d1d33d00349449932f4679144acba963ace) Co-authored-by: Shenoy Pratik --- .../helpers/add_visualization_helper.ts | 27 +++++++++++++++++++ .../custom_panels/redux/panel_slice.ts | 22 ++++++++++++++- .../components/metrics/top_menu/top_menu.tsx | 8 ++---- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/public/components/custom_panels/helpers/add_visualization_helper.ts b/public/components/custom_panels/helpers/add_visualization_helper.ts index 5c57ab53a2..1c827219b5 100644 --- a/public/components/custom_panels/helpers/add_visualization_helper.ts +++ b/public/components/custom_panels/helpers/add_visualization_helper.ts @@ -96,3 +96,30 @@ export const addVisualizationPanel = ( throw new Error('Add/Replace Visualization Error:' + error); } }; + +// Add Multiple visualizations in a Panel +export const addMultipleVisualizations = ( + savedVisualizationIds: string[], + allPanelVisualizations: VisualizationType[] +) => { + try { + let newDimensions; + let visualizationsList = [...allPanelVisualizations]; + + savedVisualizationIds.map((savedVisualizationId) => { + newDimensions = getNewVizDimensions(visualizationsList); + visualizationsList = [ + ...visualizationsList, + { + id: 'panel_viz_' + uuidv4(), + savedVisualizationId, + ...newDimensions, + }, + ]; + }); + + return visualizationsList; + } catch (error) { + throw new Error('Add Multiple Visualization Error:' + error); + } +}; diff --git a/public/components/custom_panels/redux/panel_slice.ts b/public/components/custom_panels/redux/panel_slice.ts index 339241d1ae..d13909af1d 100644 --- a/public/components/custom_panels/redux/panel_slice.ts +++ b/public/components/custom_panels/redux/panel_slice.ts @@ -17,7 +17,10 @@ import { import { coreRefs } from '../../../framework/core_refs'; import { SavedObject, SimpleSavedObject } from '../../../../../../src/core/public'; import { isNameValid } from '../helpers/utils'; -import { addVisualizationPanel } from '../helpers/add_visualization_helper'; +import { + addMultipleVisualizations, + addVisualizationPanel, +} from '../helpers/add_visualization_helper'; interface InitialState { id: string; @@ -158,6 +161,23 @@ export const addVizToPanels = (panels, vizId) => async (dispatch, getState) => { }); }; +export const addMultipleVizToPanels = (panels, vizIds) => async (dispatch, getState) => { + forEach(panels, (oldPanel) => { + const panel = getState().customPanel.panelList.find((p) => p.id === oldPanel.panel.id); + + const allVisualizations = panel!.visualizations; + + const visualizationsWithNewPanel = addMultipleVisualizations(vizIds, allVisualizations); + + const updatedPanel = { ...panel, visualizations: visualizationsWithNewPanel }; + try { + dispatch(updatePanel(updatedPanel)); + } catch (err) { + console.error(err?.body?.message || err); + } + }); +}; + export const replaceVizInPanel = (oldPanel, oldVizId, vizId) => async (dispatch, getState) => { const panel = getState().customPanel.panelList.find((p) => p.id === oldPanel.id); diff --git a/public/components/metrics/top_menu/top_menu.tsx b/public/components/metrics/top_menu/top_menu.tsx index 9bd8839c50..70a10059f7 100644 --- a/public/components/metrics/top_menu/top_menu.tsx +++ b/public/components/metrics/top_menu/top_menu.tsx @@ -27,7 +27,6 @@ import { resolutionOptions } from '../../../../common/constants/metrics'; import { MetricType } from '../../../../common/types/metrics'; import { uiSettingsService } from '../../../../common/utils'; import SavedObjects from '../../../services/saved_objects/event_analytics/saved_objects'; -import { addVizToPanels, uuidRx } from '../../custom_panels/redux/panel_slice'; import { sortMetricLayout, updateMetricsWithSelections } from '../helpers/utils'; import { allAvailableMetricsSelector, @@ -37,6 +36,7 @@ import { import { SearchBar } from '../sidebar/search_bar'; import { MetricsExportPanel } from './metrics_export_panel'; import './top_menu.scss'; +import { addMultipleVizToPanels, uuidRx } from '../../custom_panels/redux/panel_slice'; interface TopMenuProps { http: CoreStart['http']; @@ -158,7 +158,6 @@ export const TopMenu = ({ const handleSavingObjects = async () => { let savedMetricIds = []; - const savedMetricsInPanels = []; try { savedMetricIds = await Promise.all( @@ -194,6 +193,7 @@ export const TopMenu = ({ const soPanels = selectedPanelOptions.filter((panel) => uuidRx.test(panel.panel.id)); const opsPanels = selectedPanelOptions.filter((panel) => !uuidRx.test(panel.panel.id)); + dispatch(addMultipleVizToPanels(soPanels, allMetricIds)); const savedMetricsInOpsPanels = await Promise.all( opsPanels.map((panel) => { return http.post(`${CUSTOM_PANELS_API_PREFIX}/visualizations/multiple`, { @@ -204,10 +204,6 @@ export const TopMenu = ({ }); }) ); - - allMetricIds.forEach((metricId) => { - dispatch(addVizToPanels(soPanels, metricId)); - }); } catch (e) { const message = 'Issue in saving metrics to panels'; console.error(message, e); From 6338d3dc8b4a7b835c25007608b263d61f4b9a76 Mon Sep 17 00:00:00 2001 From: Peter Fitzgibbons Date: Thu, 20 Apr 2023 11:30:57 -0700 Subject: [PATCH 09/12] Test fixes for Custom Panels Signed-off-by: Peter Fitzgibbons --- .cypress/integration/3_panels.spec.ts | 93 +- .../custom_panel_view.test.tsx.snap | 6574 +++++++++-------- .../__tests__/custom_panel_view.test.tsx | 80 +- .../custom_panels/custom_panel_table.tsx | 2 +- .../explorer/save_panel/save_panel.tsx | 14 - .../components/event_analytics/home/home.tsx | 2 +- 6 files changed, 3391 insertions(+), 3374 deletions(-) diff --git a/.cypress/integration/3_panels.spec.ts b/.cypress/integration/3_panels.spec.ts index 43cc625c2b..4fac68df16 100644 --- a/.cypress/integration/3_panels.spec.ts +++ b/.cypress/integration/3_panels.spec.ts @@ -88,10 +88,10 @@ describe('Testing panels table', () => { moveToPanelHome(); }); - it('Displays error toast for invalid panel name', () => { + it.skip('Displays error toast for invalid panel name', () => { clickCreatePanelButton(); confirmModal(); - expectToastWith('Invalid Operational Panel name'); + expectToastWith('Invalid Dashboard name'); }); it('Creates a panel and redirects to the panel', () => { @@ -150,6 +150,37 @@ describe('Testing panels table', () => { cy.get('button[data-test-subj="popoverModal__deleteButton"]').click(); cy.get('h2[data-test-subj="customPanels__noPanelsHome"]').should('exist'); }); + + it('Searches panels', () => { + createLegacyPanel('Legacy Named'); + createSavedObjectPanel('Saved Object'); + cy.reload(); + cy.get('input[data-test-subj="operationalPanelSearchBar"]') + .focus() + .type('this panel should not exist', { + delay: 50, + }); + + cy.get('.euiTableCellContent__text').contains('No items found').should('exist'); + + // Search for oriignal Legacy Panel + cy.get('[aria-label="Clear input"]').click(); + cy.get('input[data-test-subj="operationalPanelSearchBar"]').focus().type(TEST_PANEL, { + delay: 50, + }); + + cy.get('a.euiLink').contains(TEST_PANEL).should('exist'); + cy.get('.euiTableRow').should('have.length', 1); + + // Search for teh Saved Object panel + cy.get('[aria-label="Clear input"]').click(); + cy.get('input[data-test-subj="operationalPanelSearchBar"]').focus().type('Saved Object', { + delay: 50, + }); + + cy.get('a.euiLink').contains('Saved Object').should('exist'); + cy.get('.euiTableRow').should('have.length', 1); + }); }); describe('with a SavedObjects Panel', () => { @@ -196,38 +227,6 @@ describe('Testing panels table', () => { cy.get('h2[data-test-subj="customPanels__noPanelsHome"]').should('exist'); }); }); - - it('Searches existing panel', () => { - createLegacyPanel(); - cy.get('input[data-test-subj="operationalPanelSearchBar"]') - .focus() - .type('this panel should not exist', { - delay: 50, - }); - - cy.get('.euiTableCellContent__text').contains('No items found').should('exist'); - - cy.get('[aria-label="Clear input"]').click(); - cy.get('input[data-test-subj="operationalPanelSearchBar"]') - .focus() - .type(TEST_PANEL + ' (copy) (rename)', { - delay: 50, - }); - - cy.get('a.euiLink') - .contains(TEST_PANEL + ' (copy) (rename)') - .should('exist'); - }); - - it('Create a panel for testing', () => { - moveToPanelHome(); - // keep a panel for testing - clickCreatePanelButton(); - cy.get('input.euiFieldText').focus().type(TEST_PANEL, { - delay: 50, - }); - cy.get('button[data-test-subj="runModalButton"]').click(); - }); }); describe('Testing a panel', () => { @@ -274,7 +273,7 @@ describe('Testing a panel', () => { cy.get(`input.euiFieldText[value="${TEST_PANEL} (copy)"]`) .focus() - .clear({force: true}) + .clear({ force: true }) .focus() .type('Renamed Panel', { delay: 200, @@ -347,9 +346,9 @@ describe('Testing a panel', () => { cy.get('h5[data-test-subj="visualizationHeader"]') .contains(PPL_VISUALIZATIONS_NAMES[1]) - .trigger('mousedown', {which: 1}) - .trigger('mousemove', {clientX: 1100, clientY: 0}) - .trigger('mouseup', {force: true}); + .trigger('mousedown', { which: 1 }) + .trigger('mousemove', { clientX: 1100, clientY: 0 }) + .trigger('mouseup', { force: true }); cy.get('button[data-test-subj="savePanelButton"]').click(); cy.wait(delay * 3); @@ -364,9 +363,9 @@ describe('Testing a panel', () => { cy.get('.react-resizable-handle') .eq(1) - .trigger('mousedown', {which: 1}) - .trigger('mousemove', {clientX: 2000, clientY: 800}) - .trigger('mouseup', {force: true}); + .trigger('mousedown', { which: 1 }) + .trigger('mousemove', { clientX: 2000, clientY: 800 }) + .trigger('mouseup', { force: true }); cy.get('button[data-test-subj="savePanelButton"]').click(); cy.wait(delay * 3); @@ -481,7 +480,7 @@ describe('Testing a panel', () => { cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').trigger('mouseover').click(); cy.wait(1000); cy.get('[data-test-subj="eventExplorer__querySaveName"]') - .clear({force: true}) + .clear({ force: true }) .type(NEW_VISUALIZATION_NAME, { delay: 200, }); @@ -537,7 +536,7 @@ describe('Clean up all test data', () => { const moveToEventsHome = () => { cy.visit(`${Cypress.env('opensearchDashboards')}/app/observability-logs#/`); - cy.wait(delay * 3); + cy.wait(6000); }; const moveToPanelHome = () => { @@ -620,7 +619,7 @@ const uuidRx = /[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4 const clickCreatePanelButton = () => cy.get('a[data-test-subj="customPanels__createNewPanels"]').click(); -const createSavedObjectPanel = () => { +const createSavedObjectPanel = (newName = TEST_PANEL) => { const result = cy .request({ method: 'POST', @@ -632,7 +631,7 @@ const createSavedObjectPanel = () => { }, body: { attributes: { - title: TEST_PANEL, + title: newName, description: '', dateCreated: 1681127334085, dateModified: 1681127334085, @@ -652,7 +651,7 @@ const createSavedObjectPanel = () => { .then((response) => console.log(response)); }; -const createLegacyPanel = () => { +const createLegacyPanel = (newName = TEST_PANEL) => { const result = cy.request({ method: 'POST', failOnStatusCode: false, @@ -661,7 +660,7 @@ const createLegacyPanel = () => { 'content-type': 'application/json;charset=UTF-8', 'osd-xsrf': true, }, - body: { panelName: TEST_PANEL }, + body: { panelName: newName }, }); }; diff --git a/public/components/custom_panels/__tests__/__snapshots__/custom_panel_view.test.tsx.snap b/public/components/custom_panels/__tests__/__snapshots__/custom_panel_view.test.tsx.snap index 8bd0d4395d..05379e0bcd 100644 --- a/public/components/custom_panels/__tests__/__snapshots__/custom_panel_view.test.tsx.snap +++ b/public/components/custom_panels/__tests__/__snapshots__/custom_panel_view.test.tsx.snap @@ -1,697 +1,581 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Panels View Component renders panel view container with visualizations 1`] = ` - + -
- -
+
+ - -
- -
- -
- + +
+ +
-

- - -
- + + +
-
- -
- - Created on - Invalid date -
- - -
- +
+ +
+ + Created on + Invalid date +
+
+ +
-
- -
- + +
- - - - -
-
- -
- - Dashboard Actions - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="none" - withTitle={true} - > -
-
- - - - - -
-
-
-
-
- -
+ + + + + Edit + + + + + + +
+
+ - - Add visualization + Dashboard Actions
} closePopover={[Function]} display="inlineBlock" hasArrow={true} - id="addVisualizationContextMenu" isOpen={false} ownFocus={true} panelPaddingSize="none" + withTitle={true} >
- -
-
-
- -
-
-

-
- -
- -
- -
- - PPL - - } - baseQuery="source = " - dslService={ - DSLService { - "fetch": [Function], - "fetchFields": [Function], - "fetchIndices": [Function], - "http": [MockFunction], - } - } - getSuggestions={[Function]} - handleQueryChange={[Function]} - handleQuerySearch={[Function]} - isDisabled={true} - key="autocomplete-search-bar" - onItemSelect={[Function]} - placeholder="Use PPL 'where' clauses to add filters on all visualizations [where Carrier = 'OpenSearch-Air']" - possibleCommands={ - Array [ - Object { - "label": "where", - }, - ] - } - query="" - tabId="panels-filter" - tempQuery="" - > -
+ + - - PPL - - } - aria-autocomplete="both" - aria-labelledby="autocomplete-4-label" - autoCapitalize="off" - autoComplete="off" - autoCorrect="off" - autoFocus={false} - data-test-subj="searchAutocompleteTextArea" - disabled={true} - enterKeyHint="search" - fullWidth={true} - id="autocomplete-textarea" - maxLength={512} - onBlur={[Function]} - onChange={[Function]} - onClick={[Function]} - onFocus={[Function]} - onKeyDown={[Function]} - placeholder="Use PPL 'where' clauses to add filters on all visualizations [where Carrier = 'OpenSearch-Air']" - spellCheck="false" - type="search" - value="" +
- - PPL - - } - fullWidth={true} - inputId="autocomplete-textarea" + -
+ Add visualization + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="addVisualizationContextMenu" + isOpen={false} + ownFocus={true} + panelPaddingSize="none" >
- - - - -
- - - -
-
- -
- -
- - -
- + + + + +
+
+ + +
+ +
+ +
+
+
+
+ +
+ +
+ +
- + PPL + + } + baseQuery="source = " + dslService={ + DSLService { + "fetch": [Function], + "fetchFields": [Function], + "fetchIndices": [Function], + "http": [MockFunction], + } + } + getSuggestions={[Function]} + handleQueryChange={[Function]} + handleQuerySearch={[Function]} + isDisabled={true} + key="autocomplete-search-bar" + onItemSelect={[Function]} + placeholder="Use PPL 'where' clauses to add filters on all visualizations [where Carrier = 'OpenSearch-Air']" + possibleCommands={ + Array [ + Object { + "label": "where", + }, + ] + } + query="" + tabId="panels-filter" + tempQuery="" >
- -
- + + PPL + + } + aria-autocomplete="both" + aria-labelledby="autocomplete-4-label" + autoCapitalize="off" + autoComplete="off" + autoCorrect="off" + autoFocus={false} + data-test-subj="searchAutocompleteTextArea" + disabled={true} + enterKeyHint="search" + fullWidth={true} + id="autocomplete-textarea" + maxLength={512} + onBlur={[Function]} + onChange={[Function]} + onClick={[Function]} + onFocus={[Function]} + onKeyDown={[Function]} + placeholder="Use PPL 'where' clauses to add filters on all visualizations [where Carrier = 'OpenSearch-Air']" + spellCheck="false" + type="search" + value="" + > + + PPL + + } + fullWidth={true} + inputId="autocomplete-textarea" + > +
- + + + +
+ + + +
+
+
+
+ +
+ + +
+ + +
+ +
+ + } + > +
+ -
-
+ - - + + + +
-
- - -
- } - iconType={false} - isCustom={true} - startDateControl={
} + + +
-
} + iconType={false} + isCustom={true} + startDateControl={
} > - -
- - + + Show dates + + + +
+ + +
-
-
-
-
- -
+
+
+ - - - - - - - - - - - -
- -
-
- -
-
-
-
- -
- - -
- -
- - -
+ + + + + + +
+ +
+ + +
+ +
+ + +
+ + +
+ +
+ + - -
-

- Start by adding your first visualization -

- -
- - -
+ Start by adding your first visualization + + +
+ + - -
- Use PPL Queries to fetch & filter observability data and create visualizations -
-
-
- -
- -
- - -
- - -
+ Use PPL Queries to fetch & filter observability data and create visualizations +
+ +
+ +
+
+
+ + - + + +
-
- - - Add visualization - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - id="addVisualizationContextMenu" - isOpen={false} - ownFocus={true} - panelPaddingSize="none" + -
-
+ - + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="addVisualizationContextMenu" + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
+
+ - - - + + + + +
-
- - -
- -
- - -
- -
- - + +
+ +
+ + +
+ +
+
+ - - - -
- - - - -
- -
- -
- -
- + maxRows={Infinity} + onDrag={[Function]} + onDragStart={[Function]} + onDragStop={[Function]} + onLayoutChange={[Function]} + onResize={[Function]} + onResizeStart={[Function]} + onResizeStop={[Function]} + preventCollision={false} + rowHeight={150} + style={Object {}} + useCSSTransforms={true} + verticalCompact={true} + width={0} + > +
+ + + + +
+ +
+ +
+
+
+
+ `; exports[`Panels View Component renders panel view container without visualizations 1`] = ` - + -
- -
+
+ - -
- -
- -
- + +
+ +
-

- - -
- + + +
-
- -
- - Created on - Invalid date -
- - -
- +
+ +
+ + Created on + Invalid date +
+
+ +
-
- -
- + +
- - - - -
-
- -
- - Dashboard Actions - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="none" - withTitle={true} - > -
-
- - - - - -
-
-
-
-
- -
+ + + + Edit + + + + + + +
+
+ - - Add visualization + Dashboard Actions
} closePopover={[Function]} display="inlineBlock" hasArrow={true} - id="addVisualizationContextMenu" isOpen={false} ownFocus={true} panelPaddingSize="none" + withTitle={true} >
+ + +
+
+ +
+
+ +
+ + + Add visualization + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="addVisualizationContextMenu" + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
+
+ + + - - + + + + +
-
- - -
- -
- -
- -

-
- -
- + +
+ +
+ +
+ + + + +
-
- -
- + +
+ + PPL + + } + baseQuery="source = " + dslService={ + DSLService { + "fetch": [Function], + "fetchFields": [Function], + "fetchIndices": [Function], + "http": [MockFunction], } - > - PPL - - } - baseQuery="source = " - dslService={ - DSLService { - "fetch": [Function], - "fetchFields": [Function], - "fetchIndices": [Function], - "http": [MockFunction], } - } - getSuggestions={[Function]} - handleQueryChange={[Function]} - handleQuerySearch={[Function]} - isDisabled={true} - key="autocomplete-search-bar" - onItemSelect={[Function]} - placeholder="Use PPL 'where' clauses to add filters on all visualizations [where Carrier = 'OpenSearch-Air']" - possibleCommands={ - Array [ - Object { - "label": "where", - }, - ] - } - query="" - tabId="panels-filter" - tempQuery="" - > -
- - PPL - - } - aria-autocomplete="both" +
- } + aria-autocomplete="both" + aria-labelledby="autocomplete-1-label" + autoCapitalize="off" + autoComplete="off" + autoCorrect="off" + autoFocus={false} + data-test-subj="searchAutocompleteTextArea" + disabled={true} + enterKeyHint="search" fullWidth={true} - inputId="autocomplete-textarea" + id="autocomplete-textarea" + maxLength={512} + onBlur={[Function]} + onChange={[Function]} + onClick={[Function]} + onFocus={[Function]} + onKeyDown={[Function]} + placeholder="Use PPL 'where' clauses to add filters on all visualizations [where Carrier = 'OpenSearch-Air']" + spellCheck="false" + type="search" + value="" > -
+ PPL + + } + fullWidth={true} + inputId="autocomplete-textarea" >
- - - - -
- -
+ - PPL - - -
- -
-
-
-
-
- -
+ PPL + + +
+ + +
+ +
+ + - - -
- -
- - } +
+ +
-
+ } > - - - - + -
-
+ - - + + + +
-
- - -
- } - iconType={false} - isCustom={true} - startDateControl={
} + + +
-
} + iconType={false} + isCustom={true} + startDateControl={
} > - -
- - + + Show dates + + + +
+ + +
-
- -
-
- -
+
+
+ - - - - - - - - - - - -
- -
- - -
-
-
- - -
- - -
- -
- - -
+ + + + + + +
+ +
+ + +
+ +
+ + +
+ + +
+ +
+ + - -
-

- Start by adding your first visualization -

- -
- - -
+ Start by adding your first visualization + + +
+ + - -
- Use PPL Queries to fetch & filter observability data and create visualizations -
-
-
- -
- -
- - -
- - -
+ Use PPL Queries to fetch & filter observability data and create visualizations +
+ +
+ +
+
+
+ + +
+ + - -
- - - Add visualization - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - id="addVisualizationContextMenu" - isOpen={false} - ownFocus={true} - panelPaddingSize="none" + -
-
+ - + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="addVisualizationContextMenu" + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
+
+ - - - + + + + +
-
- - -
- -
-
- -
- -
- - + +
+ +
+ + +
+ +
+
+ - - - -
- - - - -
- -
-
-
- -
- + maxRows={Infinity} + onDrag={[Function]} + onDragStart={[Function]} + onDragStop={[Function]} + onLayoutChange={[Function]} + onResize={[Function]} + onResizeStart={[Function]} + onResizeStop={[Function]} + preventCollision={false} + rowHeight={150} + style={Object {}} + useCSSTransforms={true} + verticalCompact={true} + width={0} + > +
+ + + + +
+ +
+ +
+ +
+ + `; diff --git a/public/components/custom_panels/__tests__/custom_panel_view.test.tsx b/public/components/custom_panels/__tests__/custom_panel_view.test.tsx index 81cb151cd6..b37601519f 100644 --- a/public/components/custom_panels/__tests__/custom_panel_view.test.tsx +++ b/public/components/custom_panels/__tests__/custom_panel_view.test.tsx @@ -20,9 +20,13 @@ import PPLService from '../../../../public/services/requests/ppl'; import DSLService from '../../../../public/services/requests/dsl'; import { coreStartMock } from '../../../../test/__mocks__/coreMocks'; import { HttpResponse } from '../../../../../../src/core/public'; +import { createStore } from '@reduxjs/toolkit'; +import { rootReducer } from '../../../framework/redux/reducers'; +import { Provider } from 'react-redux'; describe('Panels View Component', () => { configure({ adapter: new Adapter() }); + const store = createStore(rootReducer); it('renders panel view container without visualizations', async () => { httpClientMock.get = jest.fn(() => @@ -47,24 +51,26 @@ describe('Panels View Component', () => { }; const wrapper = mount( - + + + ); wrapper.update(); @@ -106,24 +112,26 @@ describe('Panels View Component', () => { }; const wrapper = mount( - + + + ); wrapper.update(); diff --git a/public/components/custom_panels/custom_panel_table.tsx b/public/components/custom_panels/custom_panel_table.tsx index a6107953b8..cf4273de65 100644 --- a/public/components/custom_panels/custom_panel_table.tsx +++ b/public/components/custom_panels/custom_panel_table.tsx @@ -222,7 +222,7 @@ export const CustomPanelTable = ({ 'Duplicate Dashboard', 'Cancel', 'Duplicate', - selectedCustomPanels[0].title + ' (copy)x', + selectedCustomPanels[0].title + ' (copy)', CREATE_PANEL_MESSAGE ) ); diff --git a/public/components/event_analytics/explorer/save_panel/save_panel.tsx b/public/components/event_analytics/explorer/save_panel/save_panel.tsx index 3d0bc0c91d..d6ee5940c4 100644 --- a/public/components/event_analytics/explorer/save_panel/save_panel.tsx +++ b/public/components/event_analytics/explorer/save_panel/save_panel.tsx @@ -61,20 +61,6 @@ export const SavePanel = ({ dispatch(fetchPanels()); }, []); - const getCustomPabnelList = async (svobj: SavedObjects) => { - const optionRes = await svobj - .fetchCustomPanels() - .then((res: any) => { - return res; - }) - .catch((error: any) => setSvpnlError(error)); - setOptions(optionRes?.panels || []); - }; - - useEffect(() => { - getCustomPabnelList(savedObjects); - }); - const onToggleChange = (e: { target: { checked: React.SetStateAction } }) => { setChecked(e.target.checked); if (e.target.checked) { diff --git a/public/components/event_analytics/home/home.tsx b/public/components/event_analytics/home/home.tsx index f82141a954..b53564ddb3 100644 --- a/public/components/event_analytics/home/home.tsx +++ b/public/components/event_analytics/home/home.tsx @@ -356,7 +356,7 @@ const EventAnalyticsHome = (props: IHomeProps) => { -

Event analytics

+

Logs

From a5cb8f2d668d4844d00cb12ecffb8a82f7a3962a Mon Sep 17 00:00:00 2001 From: Peter Fitzgibbons Date: Wed, 19 Apr 2023 13:36:46 -0700 Subject: [PATCH 10/12] Fixes * Panel View (legacy) - Duplicate - Rename Signed-off-by: Peter Fitzgibbons --- public/components/custom_panels/custom_panel_table.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/components/custom_panels/custom_panel_table.tsx b/public/components/custom_panels/custom_panel_table.tsx index cf4273de65..a6107953b8 100644 --- a/public/components/custom_panels/custom_panel_table.tsx +++ b/public/components/custom_panels/custom_panel_table.tsx @@ -222,7 +222,7 @@ export const CustomPanelTable = ({ 'Duplicate Dashboard', 'Cancel', 'Duplicate', - selectedCustomPanels[0].title + ' (copy)', + selectedCustomPanels[0].title + ' (copy)x', CREATE_PANEL_MESSAGE ) ); From c9af830fca00b4ead37538191a371f26c3a8dbf8 Mon Sep 17 00:00:00 2001 From: Peter Fitzgibbons Date: Thu, 20 Apr 2023 11:49:15 -0700 Subject: [PATCH 11/12] Fix Typo Signed-off-by: Peter Fitzgibbons --- public/components/custom_panels/custom_panel_table.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/components/custom_panels/custom_panel_table.tsx b/public/components/custom_panels/custom_panel_table.tsx index a6107953b8..cf4273de65 100644 --- a/public/components/custom_panels/custom_panel_table.tsx +++ b/public/components/custom_panels/custom_panel_table.tsx @@ -222,7 +222,7 @@ export const CustomPanelTable = ({ 'Duplicate Dashboard', 'Cancel', 'Duplicate', - selectedCustomPanels[0].title + ' (copy)x', + selectedCustomPanels[0].title + ' (copy)', CREATE_PANEL_MESSAGE ) ); From 7ccb7186f6c0d22b815a0e39798b0cfb85b89314 Mon Sep 17 00:00:00 2001 From: Peter Fitzgibbons Date: Thu, 20 Apr 2023 12:38:57 -0700 Subject: [PATCH 12/12] Fix Bad refs on customPanelViewSO Signed-off-by: Peter Fitzgibbons --- public/components/custom_panels/custom_panel_view_so.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/public/components/custom_panels/custom_panel_view_so.tsx b/public/components/custom_panels/custom_panel_view_so.tsx index 8b4a37629a..52b19ea1a3 100644 --- a/public/components/custom_panels/custom_panel_view_so.tsx +++ b/public/components/custom_panels/custom_panel_view_so.tsx @@ -53,7 +53,6 @@ import { import { EmptyPanelView } from './panel_modules/empty_panel'; import { PanelGridSO } from './panel_modules/panel_grid/panel_grid_so'; import { VisaulizationFlyoutSO } from './panel_modules/visualization_flyout/visualization_flyout_so'; -import { addVisualizationPanel } from './helpers/add_visualization_helper'; import { clonePanel, createPanel, @@ -67,9 +66,6 @@ import { setPanelSt, updatePanel, } from './redux/panel_slice'; -import { coreRefs } from '../../framework/core_refs'; - - /* * "CustomPanelsView" module used to render an Observability Dashboard