diff --git a/.cypress/integration/3_panels.spec.ts b/.cypress/integration/3_panels.spec.ts
index 43cc625c2..4fac68df1 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 8bd0d4395..05379e0bc 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"
+ >
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ >
+
+
-
-
+
-
+
+ }
+ closePopover={[Function]}
+ display="inlineBlock"
+ hasArrow={true}
+ isOpen={false}
+ ownFocus={true}
+ panelPaddingSize="m"
+ >
+
+
+
-
+
-
-
+
+
+
+
-
-
-
-
-
}
- 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"
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ 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
-
-
-
-
-
-
-
-
-
-
-
+ PPL
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
- }
+
+
+
-
+ }
>
-
-
-
-
+
-
-
+
-
+
+ }
+ closePopover={[Function]}
+ display="inlineBlock"
+ hasArrow={true}
+ isOpen={false}
+ ownFocus={true}
+ panelPaddingSize="m"
+ >
+
+
+
-
+
-
-
+
+
+
+
-
-
-
-
-
}
- 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"
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ 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 81cb151cd..b37601519 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 c0914e0b7..cf4273de6 100644
--- a/public/components/custom_panels/custom_panel_table.tsx
+++ b/public/components/custom_panels/custom_panel_table.tsx
@@ -33,22 +33,26 @@ 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';
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 {
+ clonePanel,
createPanel,
+ deletePanels,
fetchPanels,
+ isUuid,
newPanelTemplate,
renameCustomPanel,
selectPanelList,
@@ -86,11 +90,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();
@@ -133,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();
};
@@ -144,8 +164,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();
};
@@ -184,7 +213,7 @@ export const CustomPanelTable = ({
showModal();
};
- const clonePanel = () => {
+ const clonePanelModal = () => {
setModalLayout(
getCustomModal(
onClone,
@@ -254,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.tsx b/public/components/custom_panels/custom_panel_view.tsx
index 99075358b..2384d82ef 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();
},
},
{
diff --git a/public/components/custom_panels/custom_panel_view_so.tsx b/public/components/custom_panels/custom_panel_view_so.tsx
index f741d79b4..52b19ea1a 100644
--- a/public/components/custom_panels/custom_panel_view_so.tsx
+++ b/public/components/custom_panels/custom_panel_view_so.tsx
@@ -53,7 +53,19 @@ 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 { clonePanel, fetchPanel, selectPanel, setPanel, updatePanel } from './redux/panel_slice';
+import {
+ clonePanel,
+ createPanel,
+ deletePanels,
+ fetchPanel,
+ newPanelTemplate,
+ selectPanel,
+ setPanel,
+ setPanelEt,
+ setPanelId,
+ setPanelSt,
+ updatePanel,
+} from './redux/panel_slice';
/*
* "CustomPanelsView" module used to render an Observability Dashboard
@@ -87,7 +99,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,
@@ -115,7 +126,6 @@ export const CustomPanelViewSO = (props: CustomPanelViewProps) => {
parentBreadcrumbs,
childBreadcrumbs,
updateAvailabilityVizId,
- deleteCustomPanel,
cloneCustomPanel,
setToast,
onEditClick,
@@ -185,11 +195,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();
};
@@ -205,6 +224,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/home.tsx b/public/components/custom_panels/home.tsx
index 14de98e91..c113307e1 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}
/>
);
@@ -240,7 +180,6 @@ export const Home = ({
pplService={pplService}
dslService={dslService}
parentBreadcrumbs={customPanelBreadCrumbs}
- deleteCustomPanel={deleteCustomPanel}
setToast={setToast}
onEditClick={onEditClick}
page="operationalPanels"
@@ -255,7 +194,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 a68c27155..d13909af1 100644
--- a/public/components/custom_panels/redux/panel_slice.ts
+++ b/public/components/custom_panels/redux/panel_slice.ts
@@ -28,7 +28,7 @@ interface InitialState {
panelList: CustomPanelType[];
}
-export const newPanelTemplate = (newName) => ({
+export const newPanelTemplate = (newName): PanelType => ({
title: newName,
dateCreated: new Date().getTime(),
dateModified: new Date().getTime(),
@@ -69,18 +69,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
*/
@@ -134,7 +129,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 {
@@ -198,9 +193,26 @@ 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) => isUuid(id));
+ return Promise.all(soPanelIds.map((id) => savedObjectPanelsClient.delete(id)));
+};
+
+const deleteLegacyPanels = (customPanelIdList: string[]) => {
+ 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);
+};
+
+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));
};
diff --git a/public/components/event_analytics/home/home.tsx b/public/components/event_analytics/home/home.tsx
index 2d9aa1b87..e23d242f7 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