Problems: navigate to Explore button, #948

This commit is contained in:
Alexander Zobnin
2020-05-18 14:13:12 +03:00
parent 4f9202423b
commit cdb09127fb
5 changed files with 138 additions and 1 deletions

View File

@@ -1,6 +1,8 @@
import React, { PureComponent } from 'react';
import moment from 'moment';
import * as utils from '../../../datasource-zabbix/utils';
import { MODE_ITEMID } from '../../../datasource-zabbix/constants';
import { ProblemDTO, ZBXHost, ZBXGroup, ZBXEvent, ZBXTag, ZBXAlert } from '../../../datasource-zabbix/types';
import { ZBXItem, ZBXAcknowledge, GFTimeRange, RTRow } from '../../types';
import { Modal, AckProblemData } from '../Modal';
import EventTag from '../EventTag';
@@ -9,12 +11,14 @@ import ProblemStatusBar from './ProblemStatusBar';
import AcknowledgesList from './AcknowledgesList';
import ProblemTimeline from './ProblemTimeline';
import FAIcon from '../FAIcon';
import { ProblemDTO, ZBXHost, ZBXGroup, ZBXEvent, ZBXTag, ZBXAlert } from '../../../datasource-zabbix/types';
import { renderUrl } from '../../utils';
import { getLocationSrv } from '@grafana/runtime';
interface ProblemDetailsProps extends RTRow<ProblemDTO> {
rootWidth: number;
timeRange: GFTimeRange;
showTimeline?: boolean;
panelId?: number;
getProblemEvents: (problem: ProblemDTO) => Promise<ZBXEvent[]>;
getProblemAlerts: (problem: ProblemDTO) => Promise<ZBXAlert[]>;
onProblemAck?: (problem: ProblemDTO, data: AckProblemData) => Promise<any> | any;
@@ -89,6 +93,25 @@ export default class ProblemDetails extends PureComponent<ProblemDetailsProps, P
this.setState({ showAckDialog: false });
}
openInExplore = () => {
const problem = this.props.original as ProblemDTO;
const itemids = problem.items?.map(p => p.itemid).join(',');
const state: any = {
datasource: problem.datasource,
context: 'explore',
originPanelId: this.props.panelId,
queries: [{
queryType: MODE_ITEMID,
itemids: itemids,
}],
};
const exploreState = JSON.stringify(state);
const url = renderUrl('/explore', { left: exploreState });
getLocationSrv().update({ path: url, query: {} });
};
render() {
const problem = this.props.original as ProblemDTO;
const alerts = this.state.alerts;
@@ -110,6 +133,7 @@ export default class ProblemDetails extends PureComponent<ProblemDetailsProps, P
</div>
{problem.items && <ProblemItems items={problem.items} />}
</div>
<ExploreButton onClick={this.openInExplore} />
<ProblemStatusBar problem={problem} alerts={alerts} className={compactStatusBar && 'compact'} />
{problem.showAckButton &&
<div className="problem-actions">
@@ -273,3 +297,17 @@ class ProblemActionButton extends PureComponent<ProblemActionButtonProps> {
return button;
}
}
interface ExploreButtonProps {
onClick: (event?) => void;
}
const ExploreButton: React.FC<ExploreButtonProps> = ({ onClick }) => {
return (
<Tooltip placement="bottom" content="Open in Explore">
<button className="btn problem-explore-button" onClick={onClick}>
<FAIcon icon="compass" /><span>Explore</span>
</button>
</Tooltip>
);
};

View File

@@ -21,6 +21,7 @@ export interface ProblemListProps {
timeRange?: GFTimeRange;
pageSize?: number;
fontSize?: number;
panelId?: number;
getProblemEvents: (problem: ProblemDTO) => Promise<ZBXEvent[]>;
getProblemAlerts: (problem: ProblemDTO) => Promise<ZBXAlert[]>;
onProblemAck?: (problem: ProblemDTO, data: AckProblemData) => void;
@@ -168,6 +169,7 @@ export default class ProblemList extends PureComponent<ProblemListProps, Problem
rootWidth={this.rootWidth}
timeRange={this.props.timeRange}
showTimeline={panelOptions.problemTimeline}
panelId={this.props.panelId}
getProblemEvents={this.props.getProblemEvents}
getProblemAlerts={this.props.getProblemAlerts}
onProblemAck={this.handleProblemAck}

View File

@@ -395,6 +395,7 @@ export class TriggerPanelCtrl extends MetricsPanelCtrl {
loading,
pageSize,
fontSize: fontSizeProp,
panelId: ctrl.panel.id,
getProblemEvents: ctrl.getProblemEvents.bind(ctrl),
getProblemAlerts: ctrl.getProblemAlerts.bind(ctrl),
onPageSizeChange: ctrl.handlePageSizeChange.bind(ctrl),

View File

@@ -32,3 +32,73 @@ export const getNextRefIdChar = (queries: DataQuery[]): string => {
});
});
};
export type UrlQueryMap = Record<string, any>;
export function renderUrl(path: string, query: UrlQueryMap | undefined): string {
if (query && Object.keys(query).length > 0) {
path += '?' + toUrlParams(query);
}
return path;
}
function encodeURIComponentAsAngularJS(val: string, pctEncodeSpaces?: boolean) {
return encodeURIComponent(val)
.replace(/%40/gi, '@')
.replace(/%3A/gi, ':')
.replace(/%24/g, '$')
.replace(/%2C/gi, ',')
.replace(/%3B/gi, ';')
.replace(/%20/g, pctEncodeSpaces ? '%20' : '+');
}
function toUrlParams(a: any) {
const s: any[] = [];
const rbracket = /\[\]$/;
const isArray = (obj: any) => {
return Object.prototype.toString.call(obj) === '[object Array]';
};
const add = (k: string, v: any) => {
v = typeof v === 'function' ? v() : v === null ? '' : v === undefined ? '' : v;
if (typeof v !== 'boolean') {
s[s.length] = encodeURIComponentAsAngularJS(k, true) + '=' + encodeURIComponentAsAngularJS(v, true);
} else {
s[s.length] = encodeURIComponentAsAngularJS(k, true);
}
};
const buildParams = (prefix: string, obj: any) => {
let i, len, key;
if (prefix) {
if (isArray(obj)) {
for (i = 0, len = obj.length; i < len; i++) {
if (rbracket.test(prefix)) {
add(prefix, obj[i]);
} else {
buildParams(prefix, obj[i]);
}
}
} else if (obj && String(obj) === '[object Object]') {
for (key in obj) {
buildParams(prefix + '[' + key + ']', obj[key]);
}
} else {
add(prefix, obj);
}
} else if (isArray(obj)) {
for (i = 0, len = obj.length; i < len; i++) {
add(obj[i].name, obj[i].value);
}
} else {
for (key in obj) {
buildParams(key, obj[key]);
}
}
return s;
};
return buildParams('', a).join('&');
}

View File

@@ -339,6 +339,32 @@
}
}
.problem-explore-button {
&.btn {
width: 6rem;
height: 2rem;
background-image: none;
background-color: $action-button-color;
border: 1px solid darken($action-button-color, 6%);
border-radius: 1px;
margin-right: 1.6rem;
span {
color: $action-button-text-color;
}
i {
vertical-align: middle;
}
&:hover {
background-color: darken($action-button-color, 4%);
}
}
}
.problem-details-middle {
flex: 1 0 auto;
overflow: auto;