Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dashboards-observability/common/constants/explorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const TAB_CHART_TITLE = 'Visualizations';
export const TAB_EVENT_TITLE = 'Events';
export const TAB_EVENT_ID_TXT_PFX = 'main-content-events-';
export const TAB_CHART_ID_TXT_PFX = 'main-content-vis-';
export const HAS_SAVED_TIMESTAMP = 'hasSavedTimestamp';

export const DATE_PICKER_FORMAT = 'YYYY-MM-DD HH:mm:ss';
export const TIME_INTERVAL_OPTIONS = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ export const Search = (props: any) => {
isPanelTextFieldInvalid,
savedObjects,
showSavePanelOptionsList,
showSaveButton = true
showSaveButton = true,
setToast
} = props;

const [isSavePanelOpen, setIsSavePanelOpen] = useState<boolean>(false);
Expand Down Expand Up @@ -184,7 +185,10 @@ export const Search = (props: any) => {
<EuiButton
size="s"
fill
onClick={() => handleSavingObject()}>
onClick={() => {
handleSavingObject();
setIsSavePanelOpen(false);
}}>
{ "Save" }
</EuiButton>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
* GitHub history for details.
*/

import React from 'react';
import React, { useState, ReactChild } from 'react';
import { HashRouter, Route, Switch } from 'react-router-dom';
import { Toast } from '@elastic/eui/src/components/toast/global_toast_list';
import { EuiGlobalToastList } from '@elastic/eui';
import { LogExplorer } from './log_explorer';
import { Home as EventExplorerHome } from './home';
import { renderPageWithSidebar } from '../common/side_nav';
Expand All @@ -26,13 +28,28 @@ export const EventAnalytics = ({
...props
}: any) => {

const [toasts, setToasts] = useState<Array<Toast>>([]);

const eventAnalyticsBreadcrumb = {
text: 'Event analytics',
href: '#/event_analytics',
};

const setToast = (title: string, color = 'success', text?: ReactChild, side?: string) => {
if (!text) text = '';
setToasts([...toasts, { id: new Date().toISOString(), title, text, color } as Toast]);
};

return (
<HashRouter>
<>
<EuiGlobalToastList
toasts={toasts}
dismissToast={(removedToast) => {
setToasts(toasts.filter((toast) => toast.id !== removedToast.id));
}}
toastLifeTimeMs={6000}
/>
<HashRouter>
<Switch>
<Route
path={`${props.match.path}/explorer`}
Expand All @@ -52,6 +69,7 @@ export const EventAnalytics = ({
savedObjects={ savedObjects }
timestampUtils={ timestampUtils }
http={ http }
setToast={ setToast }
/>
);
}}
Expand Down Expand Up @@ -79,5 +97,6 @@ export const EventAnalytics = ({
/>
</Switch>
</HashRouter>
</>
);
}
122 changes: 81 additions & 41 deletions dashboards-observability/public/components/explorer/explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,25 @@
* GitHub history for details.
*/

import React, { useState, useMemo, useCallback, useEffect, useRef } from 'react';
import React, { useState, useMemo, useEffect, useRef } from 'react';
import { batch, useDispatch, useSelector } from 'react-redux';
import {
uniqueId,
isEmpty,
cloneDeep,
isEqual,
concat
has
} from 'lodash';
import {
FormattedMessage
} from '@osd/i18n/react';
import {
EuiText,
EuiButton,
EuiButtonIcon,
EuiTabbedContent,
EuiTabbedContentTab,
EuiFlexGroup,
EuiFlexItem,
EuiPopover,
EuiPopoverTitle,
EuiPopoverFooter,
EuiButtonEmpty,
htmlIdGenerator
} from '@elastic/eui';
import classNames from 'classnames';
import { Search } from '../common/search/search';
Expand All @@ -44,7 +38,6 @@ import { NoResults } from './no_results';
import { HitsCounter } from './hits_counter/hits_counter';
import { TimechartHeader } from './timechart_header';
import { ExplorerVisualizations } from './visualizations';
import { IndexPicker } from '../common/search/searchindex';
import {
IField,
IQueryTab
Expand All @@ -61,7 +54,8 @@ import {
UNSELECTED_FIELDS,
AVAILABLE_FIELDS,
INDEX,
TIME_INTERVAL_OPTIONS
TIME_INTERVAL_OPTIONS,
HAS_SAVED_TIMESTAMP
} from '../../../common/constants/explorer';
import { PPL_STATS_REGEX } from '../../../common/constants/shared';
import {
Expand Down Expand Up @@ -99,14 +93,21 @@ interface IExplorerProps {
tabId: string;
savedObjects: SavedObjects;
timestampUtils: TimestampUtils;
setToast: (
title: string,
color?: string,
text?: React.ReactChild | undefined,
side?: string | undefined
) => void;
}

export const Explorer = ({
pplService,
dslService,
tabId,
savedObjects,
timestampUtils
timestampUtils,
setToast
}: IExplorerProps) => {

const dispatch = useDispatch();
Expand Down Expand Up @@ -134,31 +135,20 @@ export const Explorer = ({
const countDistribution = useSelector(selectCountDistribution)[tabId];
const explorerVisualizations = useSelector(selectExplorerVisualization)[tabId];

const [selectedContentTabId, setSelectedContentTab] = useState<string>(TAB_EVENT_ID);
const [selectedContentTabId, setSelectedContentTab] = useState(TAB_EVENT_ID);
const [selectedCustomPanelOptions, setSelectedCustomPanelOptions] = useState([]);
const [selectedPanelName, setSelectedPanelName] = useState('');
const [curVisId, setCurVisId] = useState<string>('bar');
const [prevIndex, setPrevIndex] = useState<string>('');
const [isPanelTextFieldInvalid, setIsPanelTextFieldInvalid ] = useState<boolean>(false);
const [liveStreamChecked, setLiveStreamChecked] = useState<Boolean>(false);
const [isSidebarClosed, setIsSidebarClosed] = useState<Boolean>(false);
const [fixedScrollEl, setFixedScrollEl] = useState<HTMLElement | undefined>();
const [curVisId, setCurVisId] = useState('bar');
const [prevIndex, setPrevIndex] = useState('');
const [isPanelTextFieldInvalid, setIsPanelTextFieldInvalid ] = useState(false);
const [isSidebarClosed, setIsSidebarClosed] = useState(false);

const queryRef = useRef();
const selectedPanelNameRef = useRef();
const explorerFieldsRef = useRef();
queryRef.current = query;
selectedPanelNameRef.current = selectedPanelName;
explorerFieldsRef.current = explorerFields;

const fixedScrollRef = useCallback(
(node: HTMLElement) => {
if (node !== null) {
setFixedScrollEl(node);
}
},
[setFixedScrollEl]
);

const composeFinalQuery = (curQuery: any, timeField: string) => {
if (isEmpty(curQuery![RAW_QUERY])) return '';
Expand All @@ -176,7 +166,10 @@ export const Explorer = ({
const curIndex = getIndexPatternFromRawQuery(rawQueryStr);

if (isEmpty(rawQueryStr)) return;
if (isEmpty(curIndex)) return;
if (isEmpty(curIndex)) {
setToast('Query does not include vaild index.', 'danger');
return;
}

let curTimestamp = '';
let hasSavedTimestamp = false;
Expand All @@ -185,6 +178,8 @@ export const Explorer = ({
if (isEmpty(curQuery![SELECTED_TIMESTAMP]) || !isEqual(curIndex, prevIndex)) {
const savedTimestamps = await savedObjects.fetchSavedObjects({
objectId: curIndex
}).catch((error: any) => {
console.log(`Unable to get saved timestamp for this index: ${error.message}`);
});
if (savedTimestamps?.observabilityObjectList[0]?.timestamp?.name) {
// from saved objects
Expand All @@ -198,6 +193,11 @@ export const Explorer = ({
}
}

if (isEmpty(curTimestamp)) {
setToast('Index does not contain time field.', 'danger');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it intentional to not support indices without time field?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it was intentional to not support indices without a timestamp.

return;
}

// compose final query
const finalQuery = composeFinalQuery(curQuery, curTimestamp || curQuery![SELECTED_TIMESTAMP]);

Expand All @@ -206,11 +206,10 @@ export const Explorer = ({
query: {
finalQuery,
[SELECTED_TIMESTAMP]: curTimestamp || curQuery![SELECTED_TIMESTAMP],
'hasSavedTimestamp': hasSavedTimestamp
[HAS_SAVED_TIMESTAMP]: hasSavedTimestamp
}
}));


// search
if (rawQueryStr.match(PPL_STATS_REGEX)) {
getVisualizations();
Expand Down Expand Up @@ -295,16 +294,38 @@ export const Explorer = ({
type: timestamp.type,
dsl_type: 'date'
};
if (isEmpty(rawQueryStr) || isEmpty(curIndex)) return;
if (curQuery!['hasSavedTimestamp']) {
await savedObjects.updateTimestamp({
if (isEmpty(rawQueryStr) || isEmpty(curIndex)) {
setToast('Cannot override timestamp because there was no valid index found.', 'danger');
return;
}

let saveTimestampRes;
if (curQuery![HAS_SAVED_TIMESTAMP]) {
saveTimestampRes = await savedObjects.updateTimestamp({
...requests
})
.then((res: any) => {
setToast(`Timestamp has been overridden successfully.`, 'success');
return res;
})
.catch((error: any) => {
setToast(`Cannot override timestamp, error: ${error.message}`, 'danger');
});
} else {
await savedObjects.createSavedTimestamp({
saveTimestampRes = await savedObjects.createSavedTimestamp({
...requests
})
.then((res: any) => {
setToast(`Timestamp has been overridden successfully.`, 'success');
return res;
})
.catch((error: any) => {
setToast(`Cannot override timestamp, error: ${error.message}`, 'danger');
});
}

if (!has(saveTimestampRes, 'objectId')) return;

await dispatch(changeQuery({
tabId,
query: {
Expand Down Expand Up @@ -394,7 +415,6 @@ export const Explorer = ({
<section
className="dscTable dscTableFixedScroll"
aria-labelledby="documentsAriaLabel"
ref={fixedScrollRef}
>
<h2 className="euiScreenReaderOnly" id="documentsAriaLabel">
<FormattedMessage
Expand Down Expand Up @@ -514,10 +534,14 @@ export const Explorer = ({

const currQuery = queryRef.current;
const currFields = explorerFieldsRef.current;
if (isEmpty(currQuery![RAW_QUERY])) return;
if (isEmpty(currQuery![RAW_QUERY])) {
setToast('No query to save.', 'danger');
return;
};

if (isEmpty(selectedPanelNameRef.current)) {
setIsPanelTextFieldInvalid(true);
setToast('Name field cannot be empty.', 'danger');
return;
}
setIsPanelTextFieldInvalid(false);
Expand All @@ -531,6 +555,12 @@ export const Explorer = ({
dateRange: currQuery![SELECTED_DATE_RANGE],
name: selectedPanelNameRef.current,
timestamp: currQuery![SELECTED_TIMESTAMP]
})
.then((res: any) => {
setToast(`Query '${selectedPanelNameRef.current}' has been successfully saved.`, 'success');
})
.catch((error: any) => {
setToast(`Cannot save query '${selectedPanelNameRef.current}', error: ${error.message}`, 'danger');
});

// to-dos - update selected custom panel
Expand All @@ -548,14 +578,29 @@ export const Explorer = ({
type: curVisId,
name: selectedPanelNameRef.current,
timestamp: currQuery![SELECTED_TIMESTAMP]
})
.then((res: any) => {
setToast(`Visualization '${selectedPanelNameRef.current}' has been successfully saved.`, 'success');
return res;
})
.catch((error: any) => {
setToast(`Cannot save Visualization '${selectedPanelNameRef.current}', error: ${error.message}`, 'danger');
});

if (!has(savingVisRes, 'objectId')) return;

// update custom panel - visualization
if (!isEmpty(selectedCustomPanelOptions)) {

savedObjects.bulkUpdateCustomPanel({
selectedCustomPanels: selectedCustomPanelOptions,
savedVisualizationId: savingVisRes?.objectId
})
.then((res: any) => {
setToast(`Visualization '${selectedPanelNameRef.current}' has been successfully saved to operation panels.`, 'success');
})
.catch((error: any) => {
setToast(`Cannot add Visualization '${selectedPanelNameRef.current}' to operation panels, error: ${error.message}`, 'danger');
});
}
}
Expand Down Expand Up @@ -584,11 +629,6 @@ export const Explorer = ({
savedObjects={ savedObjects }
showSavePanelOptionsList={ isEqual(selectedContentTabId, TAB_CHART_ID) }
/>
{/* <IndexPicker
dslService={ dslService }
query = { query }
handleQueryChange={(query: string, index: string) => { handleQueryChange(query, index) } }
/> */}
<EuiTabbedContent
className="mainContentTabs"
initialSelectedTab={ memorizedMainContentTabs[0] }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ export const LogExplorer = ({
dslService,
savedObjects,
timestampUtils,
http
http,
setToast
}: ILogExplorerProps) => {

const dispatch = useDispatch();
Expand All @@ -78,7 +79,7 @@ export const LogExplorer = ({
const handleTabClose = (TabIdToBeClosed: string) => {

if (tabIds.length === 1) {
console.log('Have to have at least one tab');
setToast('Have to have at least one tab', 'danger');
return;
}

Expand Down Expand Up @@ -146,10 +147,10 @@ export const LogExplorer = ({
key={`explorer_${tabId}`}
pplService={ pplService }
dslService={ dslService }
http={ http }
tabId={ tabId }
savedObjects={ savedObjects }
timestampUtils={ timestampUtils }
setToast={ setToast }
/>
</>)
};
Expand Down
Loading