diff --git a/dashboards-observability/common/constants/custom_panels.ts b/dashboards-observability/common/constants/custom_panels.ts index ae72d893d..d8db8a5fe 100644 --- a/dashboards-observability/common/constants/custom_panels.ts +++ b/dashboards-observability/common/constants/custom_panels.ts @@ -12,26 +12,3 @@ export const CUSTOM_PANELS_API_PREFIX = '/api/observability/operational_panels'; export const CUSTOM_PANELS_DOCUMENTATION_URL = 'https://www.opensearch.org'; export const CREATE_PANEL_MESSAGE = 'Enter a name to describe the purpose of this custom panel.'; -export const RENAME_VISUALIZATION_MESSAGE = - 'Enter a name to describe the purpose of this visualization.'; - -export type VisualizationType = { - id: string; - title: string; - x: number; - y: number; - w: number; - h: number; - query: string; - type: string; -}; - -export type PanelType = { - name: string; - dateCreated: string; - dateModified: string; - visualizations: VisualizationType[]; - timeRange: { to: string; from: string }; - queryFilter: { query: string; language: string }; - refreshConfig: { pause: string; value: string }; -}; diff --git a/dashboards-observability/common/types/custom_panels.ts b/dashboards-observability/common/types/custom_panels.ts index 4d8f62145..36449130c 100644 --- a/dashboards-observability/common/types/custom_panels.ts +++ b/dashboards-observability/common/types/custom_panels.ts @@ -18,14 +18,11 @@ export type CustomPanelListType = { export type VisualizationType = { id: string; - title: string; + savedVisualizationId: string; x: number; y: number; w: number; h: number; - query: string; - type: string; - timeField: string; }; export type PanelType = { diff --git a/dashboards-observability/public/components/custom_panels/custom_panel_table.tsx b/dashboards-observability/public/components/custom_panels/custom_panel_table.tsx index 8c33c5005..1c1f5bd34 100644 --- a/dashboards-observability/public/components/custom_panels/custom_panel_table.tsx +++ b/dashboards-observability/public/components/custom_panels/custom_panel_table.tsx @@ -36,7 +36,6 @@ import { } from '@elastic/eui'; import React, { CSSProperties, ReactElement, useEffect, useState } from 'react'; import { ChromeBreadcrumb } from '../../../../../src/core/public'; -import { CustomPanelListType } from './home'; import { CREATE_PANEL_MESSAGE, CUSTOM_PANELS_DOCUMENTATION_URL, @@ -45,6 +44,7 @@ import { UI_DATE_FORMAT } from '../../../common/constants/shared'; import { getCustomModal, DeletePanelModal } from './helpers/modal_containers'; import moment from 'moment'; import _ from 'lodash'; +import { CustomPanelListType } from '../../../common/types/custom_panels'; const pageStyles: CSSProperties = { float: 'left', @@ -65,12 +65,11 @@ const pageStyles: CSSProperties = { * renameCustomPanel: rename function for the panel * cloneCustomPanel: clone function for the panel * deleteCustomPanelList: delete function for the panels - * setToast: create Toast function */ type Props = { loading: boolean; - fetchCustomPanels: () => Promise; + fetchCustomPanels: () => void; customPanels: Array; createCustomPanel: (newCustomPanelName: string) => void; setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => void; diff --git a/dashboards-observability/public/components/custom_panels/custom_panel_view.tsx b/dashboards-observability/public/components/custom_panels/custom_panel_view.tsx index d5373dc63..974bed04b 100644 --- a/dashboards-observability/public/components/custom_panels/custom_panel_view.tsx +++ b/dashboards-observability/public/components/custom_panels/custom_panel_view.tsx @@ -27,7 +27,6 @@ import { EuiSuperDatePicker, EuiSuperDatePickerProps, EuiTitle, - htmlIdGenerator, ShortDate, } from '@elastic/eui'; import _ from 'lodash'; @@ -35,7 +34,6 @@ import React, { useEffect, useState } from 'react'; import { CoreStart } from '../../../../../src/core/public'; import { EmptyPanelView } from './panel_modules/empty_panel'; import { - RENAME_VISUALIZATION_MESSAGE, CREATE_PANEL_MESSAGE, CUSTOM_PANELS_API_PREFIX, } from '../../../common/constants/custom_panels'; @@ -292,79 +290,22 @@ export const CustomPanelView = ({ }); }; - const cloneVisualization = ( - newVisualizationTitle: string, - pplQuery: string, - newVisualizationType: string, - newVisualizationTimeField: string - ) => { - setModalLayout( - getCustomModal( - onCloneVisualization, - closeModal, - 'Name', - 'Duplicate Visualization', - 'Cancel', - 'Duplicate', - newVisualizationTitle + ' (copy)', - RENAME_VISUALIZATION_MESSAGE, - [pplQuery, newVisualizationType, newVisualizationTimeField] - ) - ); - showModal(); - }; - - const onCloneVisualization = ( - newVisualizationTitle: string, - pplQuery: string, - newVisualizationType: string, - newVisualizationTimeField: string - ) => { + const cloneVisualization = (visualzationTitle: string, savedVisualizationId: string) => { http .post(`${CUSTOM_PANELS_API_PREFIX}/visualizations`, { body: JSON.stringify({ panelId: panelId, - newVisualization: { - id: 'panelViz_' + htmlIdGenerator()(), - title: newVisualizationTitle, - query: pplQuery, - type: newVisualizationType, - timeField: newVisualizationTimeField, - }, + savedVisualizationId: savedVisualizationId, }), }) .then(async (res) => { setPanelVisualizations(res.visualizations); - setToast(`Visualization ${newVisualizationTitle} successfully added!`, 'success'); + setToast(`Visualization ${visualzationTitle} successfully added!`, 'success'); }) .catch((err) => { - setToast(`Error in adding ${newVisualizationTitle} visualization to the panel`, 'danger'); + setToast(`Error in adding ${visualzationTitle} visualization to the panel`, 'danger'); console.error(err); }); - closeModal(); - }; - - const removeVisualization = (visualizationId: string) => { - const newVisualizationList = _.reject(panelVisualizations, { - id: visualizationId, - }); - if (newVisualizationList.length === 0) { - setEditMode(false); - http - .put(`${CUSTOM_PANELS_API_PREFIX}/visualizations/edit`, { - body: JSON.stringify({ - panelId: panelId, - visualizationParams: [], - }), - }) - .then(async (res) => { - setPanelVisualizations(res.visualizations); - }) - .catch((err) => { - console.error(err); - }); - } - setPanelVisualizations(newVisualizationList); }; //Add Visualization Button @@ -533,7 +474,7 @@ export const CustomPanelView = ({ cloneVisualization={cloneVisualization} pplFilterValue={pplFilterValue} showFlyout={showFlyout} - removeVisualization={removeVisualization} + setEditMode={setEditMode} /> )} <> diff --git a/dashboards-observability/public/components/custom_panels/helpers/utils.tsx b/dashboards-observability/public/components/custom_panels/helpers/utils.tsx index 5ebbcfbc3..50c206f7c 100644 --- a/dashboards-observability/public/components/custom_panels/helpers/utils.tsx +++ b/dashboards-observability/public/components/custom_panels/helpers/utils.tsx @@ -20,16 +20,23 @@ import React from 'react'; import { Bar } from '../../visualizations/charts/bar'; import { HorizontalBar } from '../../visualizations/charts/horizontal_bar'; import { Line } from '../../visualizations/charts/line'; +import { CoreStart } from '../../../../../../src/core/public'; +import { CUSTOM_PANELS_API_PREFIX } from '../../../../common/constants/custom_panels'; +import { VisualizationType, SavedVisualizationType } from '../../../../common/types/custom_panels'; +import { Layout } from 'react-grid-layout'; /* * "Utils" This file contains different reused functions in operational panels + * * isNameValid - Validates string to length > 0 and < 50 * convertDateTime - Converts input datetime string to required format + * mergeLayoutAndVisualizations - Function to merge current panel layout into the visualizations list * getQueryResponse - Get response of PPL query to load visualizations + * renderSavedVisualization - Fetches savedVisualization by Id and runs getQueryResponse * onTimeChange - Function to store recently used time filters and set start and end time. * isDateValid - Function to check date validity * isPPLFilterValid - Validate if the panel PPL query doesn't contain any Index/Time/Field filters - * displayVisualization - This function renders the visualzation based of its type + * displayVisualization - Function to render the visualzation based of its type */ // Name validation 0>Name<=50 @@ -50,6 +57,30 @@ export const convertDateTime = (datetime: string, isStart = true, formatted = tr return returnTime; }; +// Merges new layout into visualizations +export const mergeLayoutAndVisualizations = ( + layout: Layout[], + newVisualizationList: VisualizationType[], + setPanelVisualizations: (value: React.SetStateAction) => void +) => { + let newPanelVisualizations: VisualizationType[] = []; + + for (var i = 0; i < newVisualizationList.length; i++) { + for (var j = 0; j < layout.length; j++) { + if (newVisualizationList[i].id == layout[j].i) { + newPanelVisualizations.push({ + ...newVisualizationList[i], + x: layout[j].x, + y: layout[j].y, + w: layout[j].w, + h: layout[j].h, + }); + } + } + } + setPanelVisualizations(newPanelVisualizations); +}; + /* Builds Final Query by adding time and query filters(From panel UI) to the original visualization query * -> Final Query is as follows: * -> finalQuery = indexPartOfQuery + timeQueryFilter + panelFilterQuery + filterPartOfQuery @@ -102,6 +133,27 @@ const pplServiceRequestor = async ( }); }; +//Fetched Saved Visualization By Id +const fetchVisualizationById = async ( + http: CoreStart['http'], + savedVisualizationId: string, + setIsError: React.Dispatch> +) => { + let savedVisualization = {} as SavedVisualizationType; + await http + .get(`${CUSTOM_PANELS_API_PREFIX}/visualizations/${savedVisualizationId}`) + .then((res) => { + savedVisualization = res.visualization; + }) + .catch((err) => { + const errorMessage = 'Issue in fetching the saved Visualization by Id'; + setIsError(errorMessage); + console.error(errorMessage, err); + }); + + return savedVisualization; +}; + // Get PPL Query Response export const getQueryResponse = ( pplService: PPLService, @@ -122,7 +174,9 @@ export const getQueryResponse = ( try { finalQuery = queryAccumulator(query, timestampField, startTime, endTime, filterQuery); } catch (error) { - console.error('Issue in building final query', error.stack); + const errorMessage = 'Issue in building final query'; + setIsError(errorMessage); + console.error(errorMessage, error); setIsLoading(false); return; } @@ -130,6 +184,48 @@ export const getQueryResponse = ( pplServiceRequestor(pplService, finalQuery, type, setVisualizationData, setIsLoading, setIsError); }; +// Fetches savedVisualization by Id and runs getQueryResponse +export const renderSavedVisualization = async ( + http: CoreStart['http'], + pplService: PPLService, + savedVisualizationId: string, + startTime: string, + endTime: string, + filterQuery: string, + setVisualizationTitle: React.Dispatch>, + setVisualizationType: React.Dispatch>, + setVisualizationData: React.Dispatch>, + setIsLoading: React.Dispatch>, + setIsError: React.Dispatch> +) => { + setIsLoading(true); + setIsError(''); + + let visualization = {} as SavedVisualizationType; + visualization = await fetchVisualizationById(http, savedVisualizationId, setIsError); + + if (visualization.name) { + setVisualizationTitle(visualization.name); + } + + if (visualization.type) { + setVisualizationType(visualization.type); + } + + getQueryResponse( + pplService, + visualization.query, + visualization.type, + startTime, + endTime, + setVisualizationData, + setIsLoading, + setIsError, + filterQuery, + visualization.timeField + ); +}; + // Function to store recently used time filters and set start and end time. export const onTimeChange = ( start: ShortDate, diff --git a/dashboards-observability/public/components/custom_panels/home.tsx b/dashboards-observability/public/components/custom_panels/home.tsx index 4742b0cd6..a5f10d330 100644 --- a/dashboards-observability/public/components/custom_panels/home.tsx +++ b/dashboards-observability/public/components/custom_panels/home.tsx @@ -59,7 +59,8 @@ export const Home = ({ http, chrome, parentBreadcrumb, pplService, renderProps } // Fetches all saved Custom Panels const fetchCustomPanels = () => { - return http + setLoading(true); + http .get(`${CUSTOM_PANELS_API_PREFIX}/panels`) .then((res) => { setcustomPanelData(res.panels); @@ -67,6 +68,7 @@ export const Home = ({ http, chrome, parentBreadcrumb, pplService, renderProps } .catch((err) => { console.error('Issue in fetching the operational panels', err.body.message); }); + setLoading(false); }; // Creates a new CustomPanel diff --git a/dashboards-observability/public/components/custom_panels/panel_modules/panel_grid/panel_grid.tsx b/dashboards-observability/public/components/custom_panels/panel_modules/panel_grid/panel_grid.tsx index 039f9f4e3..beab4e7c1 100644 --- a/dashboards-observability/public/components/custom_panels/panel_modules/panel_grid/panel_grid.tsx +++ b/dashboards-observability/public/components/custom_panels/panel_modules/panel_grid/panel_grid.tsx @@ -19,6 +19,7 @@ import { VisualizationContainer } from '../visualiation_container'; import { VisualizationType } from '../../../../../common/types/custom_panels'; import { CUSTOM_PANELS_API_PREFIX } from '../../../../../common/constants/custom_panels'; import './panel_grid.scss'; +import { mergeLayoutAndVisualizations } from '../../helpers/utils'; // HOC container to provide dynamic width for Grid layout const ResponsiveGridLayout = WidthProvider(Responsive); @@ -29,6 +30,7 @@ const ResponsiveGridLayout = WidthProvider(Responsive); * Props taken in as params are: * http: http core service; * chrome: chrome core service; + * panelId: OpenPanel Id * panelVisualizations: list of panel visualizations * setPanelVisualizations: function to set panel visualizations * editMode: boolean to check if the panel is in edit mode @@ -39,7 +41,7 @@ const ResponsiveGridLayout = WidthProvider(Responsive); * cloneVisualization: function to clone a visualization in panel * pplFilterValue: string with panel PPL filter value * showFlyout: function to show the flyout - * removeVisualization: function to remove all the visualizations + * setEditMode: function to set edit mode in panel */ type Props = { @@ -53,15 +55,10 @@ type Props = { startTime: string; endTime: string; onRefresh: boolean; - cloneVisualization: ( - newVisualizationTitle: string, - pplQuery: string, - newVisualizationType: string, - newVisualizationTimeField: string - ) => void; + cloneVisualization: (visualzationTitle: string, savedVisualizationId: string) => void; pplFilterValue: string; showFlyout: (isReplacement?: boolean | undefined, replaceVizId?: string | undefined) => void; - removeVisualization: (visualizationId: string) => void; + setEditMode: React.Dispatch>; }; export const PanelGrid = ({ @@ -78,7 +75,7 @@ export const PanelGrid = ({ cloneVisualization, pplFilterValue, showFlyout, - removeVisualization, + setEditMode, }: Props) => { const [layout, setLayout] = useState([]); const [editedLayout, setEditedLayout] = useState([]); @@ -105,6 +102,33 @@ export const PanelGrid = ({ setLayout(tempLayout); }; + // remove visualization from panel in edit mode + const removeVisualization = (visualizationId: string) => { + const newVisualizationList = _.reject(panelVisualizations, { + id: visualizationId, + }); + + if (newVisualizationList.length === 0) { + setEditMode(false); + http + .put(`${CUSTOM_PANELS_API_PREFIX}/visualizations/edit`, { + body: JSON.stringify({ + panelId: panelId, + visualizationParams: [], + }), + }) + .then(async (res) => { + setPanelVisualizations(res.visualizations); + return; + }) + .catch((err) => { + console.error(err); + }); + } + mergeLayoutAndVisualizations(editedLayout, newVisualizationList, setPanelVisualizations); + }; + + // Save Visualization Layouts when not in edit mode anymore (after users saves the panel) const saveVisualizationLayouts = async (panelId: string, visualizationParams: any) => { return http .put(`${CUSTOM_PANELS_API_PREFIX}/visualizations/edit`, { @@ -158,12 +182,10 @@ export const PanelGrid = ({ {panelVisualizations.map((panelVisualization: VisualizationType) => (
void; + cloneVisualization: (visualzationTitle: string, savedVisualizationId: string) => void; pplFilterValue: string; showFlyout: (isReplacement?: boolean | undefined, replaceVizId?: string | undefined) => void; removeVisualization: (visualizationId: string) => void; }; export const VisualizationContainer = ({ + http, editMode, visualizationId, - visualizationTitle, + savedVisualizationId, pplService, - query, - type, - timeField, fromTime, toTime, onRefresh, @@ -86,6 +81,8 @@ export const VisualizationContainer = ({ }: Props) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); const [disablePopover, setDisablePopover] = useState(false); + const [visualizationTitle, setVisualizationTitle] = useState(''); + const [visualizationType, setVisualizationType] = useState(''); const [visualizationData, setVisualizationData] = useState([]); const [isLoading, setIsLoading] = useState(true); const [isError, setIsError] = useState(''); @@ -108,7 +105,7 @@ export const VisualizationContainer = ({ disabled={disablePopover} onClick={() => { closeActionsMenu(); - cloneVisualization(visualizationTitle, query, type, timeField); + cloneVisualization(visualizationTitle, savedVisualizationId); }} > Duplicate @@ -116,17 +113,18 @@ export const VisualizationContainer = ({ ]; const loadVisaulization = async () => { - await getQueryResponse( + await renderSavedVisualization( + http, pplService, - query, - type, + savedVisualizationId, fromTime, toTime, + pplFilterValue, + setVisualizationTitle, + setVisualizationType, setVisualizationData, setIsLoading, - setIsError, - pplFilterValue, - timeField + setIsError ); }; @@ -149,11 +147,11 @@ export const VisualizationContainer = ({
) : ( - displayVisualization(visualizationData, type) + displayVisualization(visualizationData, visualizationType) )} ), - [onRefresh, isLoading, isError, visualizationData, type] + [onRefresh, isLoading, isError, visualizationData, visualizationType] ); useEffect(() => { diff --git a/dashboards-observability/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.tsx b/dashboards-observability/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.tsx index 4b90d439b..75f249133 100644 --- a/dashboards-observability/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.tsx +++ b/dashboards-observability/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.tsx @@ -29,7 +29,6 @@ import { EuiSpacer, EuiText, EuiTitle, - htmlIdGenerator, ShortDate, } from '@elastic/eui'; import _ from 'lodash'; @@ -143,14 +142,8 @@ export const VisaulizationFlyout = ({ .post(`${CUSTOM_PANELS_API_PREFIX}/visualizations/replace`, { body: JSON.stringify({ panelId: panelId, + savedVisualizationId: selectValue, oldVisualizationId: replaceVisualizationId, - newVisualization: { - id: 'panelViz_' + htmlIdGenerator()(), - title: newVisualizationTitle, - query: pplQuery, - type: newVisualizationType, - timeField: newVisualizationTimeField, - }, }), }) .then(async (res) => { @@ -166,13 +159,7 @@ export const VisaulizationFlyout = ({ .post(`${CUSTOM_PANELS_API_PREFIX}/visualizations`, { body: JSON.stringify({ panelId: panelId, - newVisualization: { - id: 'panelViz_' + htmlIdGenerator()(), - title: newVisualizationTitle, - query: pplQuery, - type: newVisualizationType, - timeField: newVisualizationTimeField, - }, + savedVisualizationId: selectValue, }), }) .then(async (res) => { diff --git a/dashboards-observability/server/adaptors/custom_panels/custom_panel_adaptor.ts b/dashboards-observability/server/adaptors/custom_panels/custom_panel_adaptor.ts index f5992c712..13052e93c 100644 --- a/dashboards-observability/server/adaptors/custom_panels/custom_panel_adaptor.ts +++ b/dashboards-observability/server/adaptors/custom_panels/custom_panel_adaptor.ts @@ -9,6 +9,7 @@ * GitHub history for details. */ +import { v4 as uuidv4 } from 'uuid'; import { PanelType, VisualizationType } from '../../../common/types/custom_panels'; import { ILegacyScopedClusterClient } from '../../../../../src/core/server'; @@ -190,8 +191,8 @@ export class CustomPanelsAdaptor { } }; - // gets list of panels stored in index - viewSavedVisualiationList = async (client: ILegacyScopedClusterClient) => { + // gets all saved visualizations + getAllSavedVisualizations = async (client: ILegacyScopedClusterClient) => { try { const response = await client.callAsCurrentUser('observability.getObject', { objectType: 'savedVisualization', @@ -208,6 +209,28 @@ export class CustomPanelsAdaptor { } }; + // gets list of savedVisualizations by Id + getSavedVisualizationById = async ( + client: ILegacyScopedClusterClient, + savedVisualizationId: string + ) => { + try { + const response = await client.callAsCurrentUser('observability.getObjectById', { + objectId: savedVisualizationId, + }); + const visualization = response.observabilityObjectList[0]; + return { + id: visualization.objectId, + name: visualization.savedVisualization.name, + query: visualization.savedVisualization.query, + type: visualization.savedVisualization.type, + timeField: visualization.savedVisualization.selected_timestamp.name, + }; + } catch (error) { + throw new Error('Fetch Saved Visualizations By Id Error:' + error); + } + }; + //Get All Visualizations from a Panel //Add Visualization getVisualizations = async (client: ILegacyScopedClusterClient, panelId: string) => { @@ -241,13 +264,7 @@ export class CustomPanelsAdaptor { addVisualization = async ( client: ILegacyScopedClusterClient, panelId: string, - newVisualization: { - id: string; - title: string; - query: string; - type: string; - timeField: string; - }, + savedVisualizationId: string, oldVisualizationId?: string ) => { try { @@ -274,35 +291,11 @@ export class CustomPanelsAdaptor { } const newPanelVisualizations = [ ...visualizationsList, - { ...newVisualization, ...newDimensions }, - ]; - const updatePanelResponse = await this.updatePanel(client, panelId, { - visualizations: newPanelVisualizations, - }); - return newPanelVisualizations; - } catch (error) { - throw new Error('Add/Replace Visualization Error:' + error); - } - }; - - //Add Visualization in the Panel from Event Explorer - addVisualizationFromEvents = async ( - client: ILegacyScopedClusterClient, - panelId: string, - paramVisualization: { - id: string; - title: string; - query: string; - type: string; - timeField: string; - } - ) => { - try { - const allPanelVisualizations = await this.getVisualizations(client, panelId); - const newDimensions = this.getNewVizDimensions(allPanelVisualizations); - const newPanelVisualizations = [ - ...allPanelVisualizations, - { ...paramVisualization, ...newDimensions }, + { + id: 'panel_viz_' + uuidv4(), + savedVisualizationId: savedVisualizationId, + ...newDimensions, + }, ]; const updatePanelResponse = await this.updatePanel(client, panelId, { visualizations: newPanelVisualizations, @@ -313,26 +306,6 @@ export class CustomPanelsAdaptor { } }; - //Delete a Visualization in the Panel - deleteVisualization = async ( - client: ILegacyScopedClusterClient, - panelId: string, - visualizationId: string - ) => { - try { - const allPanelVisualizations = await this.getVisualizations(client, panelId); - const filteredPanelVisualizations = allPanelVisualizations.filter( - (panelVisualization: VisualizationType) => panelVisualization.id != visualizationId - ); - const updatePanelResponse = await this.updatePanel(client, panelId, { - visualizations: filteredPanelVisualizations, - }); - return filteredPanelVisualizations; - } catch (error) { - throw new Error('Delete Visualization Error:' + error); - } - }; - //Edits all Visualizations in the Panel editVisualization = async ( client: ILegacyScopedClusterClient, diff --git a/dashboards-observability/server/routes/custom_panels/visualizations_router.ts b/dashboards-observability/server/routes/custom_panels/visualizations_router.ts index c935b9d0c..584d3f30c 100644 --- a/dashboards-observability/server/routes/custom_panels/visualizations_router.ts +++ b/dashboards-observability/server/routes/custom_panels/visualizations_router.ts @@ -36,7 +36,7 @@ export function VisualizationsRouter(router: IRouter) { request ); try { - const visualizationList = await customPanelBackend.viewSavedVisualiationList( + const visualizationList = await customPanelBackend.getAllSavedVisualizations( opensearchNotebooksClient ); return response.ok({ @@ -54,20 +54,13 @@ export function VisualizationsRouter(router: IRouter) { } ); - // Add a new visualization to the panel - router.post( + // get all saved visualizations by Id + router.get( { - path: `${API_PREFIX}/visualizations`, + path: `${API_PREFIX}/visualizations/{savedVisualizationId}`, validate: { - body: schema.object({ - panelId: schema.string(), - newVisualization: schema.object({ - id: schema.string(), - title: schema.string(), - query: schema.string(), - type: schema.string(), - timeField: schema.string(), - }), + params: schema.object({ + savedVisualizationId: schema.string(), }), }, }, @@ -79,21 +72,18 @@ export function VisualizationsRouter(router: IRouter) { const opensearchNotebooksClient: ILegacyScopedClusterClient = context.observability_plugin.observabilityClient.asScoped( request ); - try { - const newVisualizations = await customPanelBackend.addVisualization( + const savedVisualization = await customPanelBackend.getSavedVisualizationById( opensearchNotebooksClient, - request.body.panelId, - request.body.newVisualization + request.params.savedVisualizationId ); return response.ok({ body: { - message: 'Visualization Added', - visualizations: newVisualizations, + visualization: savedVisualization, }, }); } catch (error) { - console.error('Issue in adding visualization:', error); + console.error('Issue in fetching saved visualizations by ids:', error); return response.custom({ statusCode: error.statusCode || 500, body: error.message, @@ -102,22 +92,14 @@ export function VisualizationsRouter(router: IRouter) { } ); - // Add a new visualization to panel from event explorer - // NOTE: This is a separate endpoint for adding event explorer visualizations to Operational Panels - // Please use `id = 'panelViz_' + htmlIdGenerator()()` to create unique visualization Id + // Add a new visualization to the panel router.post( { - path: `${API_PREFIX}/visualizations/event_explorer`, + path: `${API_PREFIX}/visualizations`, validate: { body: schema.object({ panelId: schema.string(), - newVisualization: schema.object({ - id: schema.string(), - title: schema.string(), - query: schema.string(), - type: schema.string(), - timeField: schema.string(), - }), + savedVisualizationId: schema.string(), }), }, }, @@ -131,10 +113,10 @@ export function VisualizationsRouter(router: IRouter) { ); try { - const newVisualizations = await customPanelBackend.addVisualizationFromEvents( + const newVisualizations = await customPanelBackend.addVisualization( opensearchNotebooksClient, request.body.panelId, - request.body.newVisualization + request.body.savedVisualizationId ); return response.ok({ body: { @@ -159,14 +141,8 @@ export function VisualizationsRouter(router: IRouter) { validate: { body: schema.object({ panelId: schema.string(), + savedVisualizationId: schema.string(), oldVisualizationId: schema.string(), - newVisualization: schema.object({ - id: schema.string(), - title: schema.string(), - query: schema.string(), - type: schema.string(), - timeField: schema.string(), - }), }), }, }, @@ -183,7 +159,7 @@ export function VisualizationsRouter(router: IRouter) { const newVisualizations = await customPanelBackend.addVisualization( opensearchNotebooksClient, request.body.panelId, - request.body.newVisualization, + request.body.savedVisualizationId, request.body.oldVisualizationId ); return response.ok({ @@ -202,48 +178,6 @@ export function VisualizationsRouter(router: IRouter) { } ); - // Delete an existing visualization - router.delete( - { - path: `${API_PREFIX}/visualizations/{panelId}/{visualizationId}`, - validate: { - params: schema.object({ - panelId: schema.string(), - visualizationId: schema.string(), - }), - }, - }, - async ( - context, - request, - response - ): Promise> => { - const opensearchNotebooksClient: ILegacyScopedClusterClient = context.observability_plugin.observabilityClient.asScoped( - request - ); - - try { - const newVisualizations = await customPanelBackend.deleteVisualization( - opensearchNotebooksClient, - request.params.panelId, - request.params.visualizationId - ); - return response.ok({ - body: { - message: 'Visualization Deleted', - visualizations: newVisualizations, - }, - }); - } catch (error) { - console.error('Issue in deleting visualization:', error); - return response.custom({ - statusCode: error.statusCode || 500, - body: error.message, - }); - } - } - ); - // changes the position of the mentioned visualizations // Also removes the visualiations not mentioned router.put(