mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: More typescript strict null fixes, going for sub 200 (#26134)
* Chore: Fix typescript strict null errors * Added new limit * Fixed ts issue * fixed tests * trying to fix type inference * Fixing more ts errors * Revert tsconfig option * Fix * Fixed code * More fixes * fix tests * Updated snapshot * Chore: More ts strict null fixes * More fixes in some really messed up azure config components * More fixes, current count: 441 * 419 * More fixes * Fixed invalid initial state in explore * Fixing tests * Fixed tests * Explore fix * More fixes * Progress * Sub 300 * Now at 218 * Progress * Update * Progress * Updated tests * at 159 * fixed tests * Fixed test
This commit is contained in:
@@ -416,11 +416,9 @@ export interface DataQueryError {
|
||||
export interface DataQueryRequest<TQuery extends DataQuery = DataQuery> {
|
||||
requestId: string; // Used to identify results and optionally cancel the request in backendSrv
|
||||
|
||||
dashboardId: number;
|
||||
interval: string;
|
||||
intervalMs?: number;
|
||||
intervalMs: number;
|
||||
maxDataPoints?: number;
|
||||
panelId: number;
|
||||
range: TimeRange;
|
||||
reverse?: boolean;
|
||||
scopedVars: ScopedVars;
|
||||
@@ -432,6 +430,8 @@ export interface DataQueryRequest<TQuery extends DataQuery = DataQuery> {
|
||||
exploreMode?: ExploreMode;
|
||||
rangeRaw?: RawTimeRange;
|
||||
timeInfo?: string; // The query time description (blue text in the upper right)
|
||||
panelId?: number;
|
||||
dashboardId?: number;
|
||||
|
||||
// Request Timing
|
||||
startTime: number;
|
||||
|
||||
@@ -164,7 +164,7 @@ export interface PanelOptionsEditorConfig<TOptions, TSettings = any, TValue = an
|
||||
*/
|
||||
export interface PanelMenuItem {
|
||||
type?: 'submenu' | 'divider';
|
||||
text?: string;
|
||||
text: string;
|
||||
iconClassName?: string;
|
||||
onClick?: (event: React.MouseEvent<any>) => void;
|
||||
shortcut?: string;
|
||||
|
||||
@@ -9,7 +9,7 @@ import { selectors } from '@grafana/e2e-selectors';
|
||||
export interface Props {
|
||||
onChange: (ds: DataSourceSelectItem) => void;
|
||||
datasources: DataSourceSelectItem[];
|
||||
current?: DataSourceSelectItem;
|
||||
current?: DataSourceSelectItem | null;
|
||||
hideTextValue?: boolean;
|
||||
onBlur?: () => void;
|
||||
autoFocus?: boolean;
|
||||
|
||||
@@ -67,6 +67,7 @@ export interface GetExploreUrlArguments {
|
||||
datasourceSrv: DataSourceSrv;
|
||||
timeSrv: TimeSrv;
|
||||
}
|
||||
|
||||
export async function getExploreUrl(args: GetExploreUrlArguments): Promise<string | undefined> {
|
||||
const { panel, panelTargets, panelDatasource, datasourceSrv, timeSrv } = args;
|
||||
let exploreDatasource = panelDatasource;
|
||||
@@ -302,7 +303,7 @@ export function ensureQueries(queries?: DataQuery[]): DataQuery[] {
|
||||
}
|
||||
return allQueries;
|
||||
}
|
||||
return [{ ...generateEmptyQuery(queries) }];
|
||||
return [{ ...generateEmptyQuery(queries ?? []) }];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,7 +357,7 @@ export function clearHistory(datasourceId: string) {
|
||||
store.delete(historyKey);
|
||||
}
|
||||
|
||||
export const getQueryKeys = (queries: DataQuery[], datasourceInstance: DataSourceApi): string[] => {
|
||||
export const getQueryKeys = (queries: DataQuery[], datasourceInstance?: DataSourceApi | null): string[] => {
|
||||
const queryKeys = queries.reduce<string[]>((newQueryKeys, query, index) => {
|
||||
const primaryKey = datasourceInstance && datasourceInstance.name ? datasourceInstance.name : query.key;
|
||||
return newQueryKeys.concat(`${primaryKey}-${index}`);
|
||||
@@ -367,8 +368,8 @@ export const getQueryKeys = (queries: DataQuery[], datasourceInstance: DataSourc
|
||||
|
||||
export const getTimeRange = (timeZone: TimeZone, rawRange: RawTimeRange): TimeRange => {
|
||||
return {
|
||||
from: dateMath.parse(rawRange.from, false, timeZone as any),
|
||||
to: dateMath.parse(rawRange.to, true, timeZone as any),
|
||||
from: dateMath.parse(rawRange.from, false, timeZone as any)!,
|
||||
to: dateMath.parse(rawRange.to, true, timeZone as any)!,
|
||||
raw: rawRange,
|
||||
};
|
||||
};
|
||||
@@ -402,13 +403,13 @@ const parseRawTime = (value: any): TimeFragment | null => {
|
||||
|
||||
export const getTimeRangeFromUrl = (range: RawTimeRange, timeZone: TimeZone): TimeRange => {
|
||||
const raw = {
|
||||
from: parseRawTime(range.from),
|
||||
to: parseRawTime(range.to),
|
||||
from: parseRawTime(range.from)!,
|
||||
to: parseRawTime(range.to)!,
|
||||
};
|
||||
|
||||
return {
|
||||
from: dateMath.parse(raw.from, false, timeZone as any),
|
||||
to: dateMath.parse(raw.to, true, timeZone as any),
|
||||
from: dateMath.parse(raw.from, false, timeZone as any)!,
|
||||
to: dateMath.parse(raw.to, true, timeZone as any)!,
|
||||
raw,
|
||||
};
|
||||
};
|
||||
@@ -536,13 +537,13 @@ export const convertToWebSocketUrl = (url: string) => {
|
||||
return `${backend}${url}`;
|
||||
};
|
||||
|
||||
export const stopQueryState = (querySubscription: Unsubscribable) => {
|
||||
export const stopQueryState = (querySubscription: Unsubscribable | undefined) => {
|
||||
if (querySubscription) {
|
||||
querySubscription.unsubscribe();
|
||||
}
|
||||
};
|
||||
|
||||
export function getIntervals(range: TimeRange, lowLimit: string, resolution?: number): IntervalValues {
|
||||
export function getIntervals(range: TimeRange, lowLimit?: string, resolution?: number): IntervalValues {
|
||||
if (!resolution) {
|
||||
return { interval: '1s', intervalMs: 1000 };
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import _ from 'lodash';
|
||||
import { DataQuery } from '@grafana/data';
|
||||
|
||||
export const getNextRefIdChar = (queries: DataQuery[]): string | undefined => {
|
||||
export const getNextRefIdChar = (queries: DataQuery[]): string => {
|
||||
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
|
||||
return _.find(letters, refId => {
|
||||
return _.every(queries, other => {
|
||||
return other.refId !== refId;
|
||||
});
|
||||
});
|
||||
return (
|
||||
_.find(letters, refId => {
|
||||
return _.every(queries, other => {
|
||||
return other.refId !== refId;
|
||||
});
|
||||
}) ?? 'NA'
|
||||
);
|
||||
};
|
||||
|
||||
export function addQuery(queries: DataQuery[], query?: Partial<DataQuery>): DataQuery[] {
|
||||
|
||||
@@ -121,7 +121,10 @@ export class AnnotationsSrv {
|
||||
promises.push(
|
||||
datasourcePromise
|
||||
.then((datasource: DataSourceApi) => {
|
||||
// issue query against data source
|
||||
if (!datasource.annotationQuery) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return datasource.annotationQuery({
|
||||
range,
|
||||
rangeRaw: range.raw,
|
||||
|
||||
@@ -17,7 +17,7 @@ interface Props {
|
||||
dashboard: DashboardModel;
|
||||
panel: PanelModel;
|
||||
plugin?: PanelPlugin | null;
|
||||
defaultTab: InspectTab;
|
||||
defaultTab?: InspectTab;
|
||||
tabs: Array<{ label: string; value: InspectTab }>;
|
||||
// The last raw response
|
||||
data?: PanelData;
|
||||
|
||||
@@ -15,7 +15,7 @@ import { updateLocation } from 'app/core/actions';
|
||||
interface OwnProps {
|
||||
dashboard: DashboardModel;
|
||||
panel: PanelModel;
|
||||
defaultTab: InspectTab;
|
||||
defaultTab?: InspectTab;
|
||||
}
|
||||
|
||||
export interface ConnectedProps {
|
||||
|
||||
@@ -8,7 +8,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export const Annotations: FunctionComponent<Props> = ({ annotations, onAnnotationChanged }) => {
|
||||
const [visibleAnnotations, setVisibleAnnotations] = useState([]);
|
||||
const [visibleAnnotations, setVisibleAnnotations] = useState<any>([]);
|
||||
useEffect(() => {
|
||||
setVisibleAnnotations(annotations.filter(annotation => annotation.hide !== true));
|
||||
}, [annotations]);
|
||||
@@ -19,7 +19,7 @@ export const Annotations: FunctionComponent<Props> = ({ annotations, onAnnotatio
|
||||
|
||||
return (
|
||||
<>
|
||||
{visibleAnnotations.map(annotation => {
|
||||
{visibleAnnotations.map((annotation: any) => {
|
||||
return (
|
||||
<div
|
||||
key={annotation.name}
|
||||
|
||||
@@ -13,35 +13,33 @@ export interface Props {
|
||||
}
|
||||
|
||||
export const DashboardLinks: FC<Props> = ({ dashboard }) => {
|
||||
if (!dashboard.links.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
dashboard.links.length > 0 && (
|
||||
<>
|
||||
{dashboard.links.map((link: DashboardLink, index: number) => {
|
||||
const linkInfo = getLinkSrv().getAnchorInfo(link);
|
||||
const key = `${link.title}-$${index}`;
|
||||
<>
|
||||
{dashboard.links.map((link: DashboardLink, index: number) => {
|
||||
const linkInfo = getLinkSrv().getAnchorInfo(link);
|
||||
const key = `${link.title}-$${index}`;
|
||||
|
||||
if (link.type === 'dashboards') {
|
||||
return <DashboardLinksDashboard key={key} link={link} linkInfo={linkInfo} dashboardId={dashboard.id} />;
|
||||
}
|
||||
if (link.type === 'dashboards') {
|
||||
return <DashboardLinksDashboard key={key} link={link} linkInfo={linkInfo} dashboardId={dashboard.id} />;
|
||||
}
|
||||
|
||||
const linkElement = (
|
||||
<a
|
||||
className="gf-form-label"
|
||||
href={sanitizeUrl(linkInfo.href)}
|
||||
target={link.targetBlank ? '_blank' : '_self'}
|
||||
>
|
||||
<Icon name={iconMap[link.icon] as IconName} style={{ marginRight: '4px' }} />
|
||||
<span>{sanitize(linkInfo.title)}</span>
|
||||
</a>
|
||||
);
|
||||
const linkElement = (
|
||||
<a className="gf-form-label" href={sanitizeUrl(linkInfo.href)} target={link.targetBlank ? '_blank' : '_self'}>
|
||||
<Icon name={iconMap[link.icon] as IconName} style={{ marginRight: '4px' }} />
|
||||
<span>{sanitize(linkInfo.title)}</span>
|
||||
</a>
|
||||
);
|
||||
|
||||
return (
|
||||
<div key={key} className="gf-form">
|
||||
{link.tooltip ? <Tooltip content={link.tooltip}>{linkElement}</Tooltip> : linkElement}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)
|
||||
return (
|
||||
<div key={key} className="gf-form">
|
||||
{link.tooltip ? <Tooltip content={link.tooltip}>{linkElement}</Tooltip> : linkElement}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import { GrafanaTheme, DataFrame } from '@grafana/data';
|
||||
|
||||
interface TransformationEditorProps {
|
||||
name: string;
|
||||
description: string;
|
||||
description?: string;
|
||||
editor?: JSX.Element;
|
||||
input: DataFrame[];
|
||||
output?: DataFrame[];
|
||||
@@ -32,9 +32,7 @@ export const TransformationEditor = ({ editor, input, output, debugMode }: Trans
|
||||
</div>
|
||||
<div className={styles.debug}>
|
||||
<div className={styles.debugTitle}>Transformation output data</div>
|
||||
<div className={styles.debugJson}>
|
||||
<JSONFormatter json={output} />
|
||||
</div>
|
||||
<div className={styles.debugJson}>{output && <JSONFormatter json={output} />}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { QueryOperationAction } from 'app/core/components/QueryOperationRow/Quer
|
||||
|
||||
interface TransformationOperationRowProps {
|
||||
name: string;
|
||||
description: string;
|
||||
description?: string;
|
||||
editor?: JSX.Element;
|
||||
onRemove: () => void;
|
||||
input: DataFrame[];
|
||||
|
||||
@@ -35,7 +35,7 @@ export interface Props {
|
||||
dashboard: DashboardModel;
|
||||
plugin: PanelPlugin;
|
||||
isViewing: boolean;
|
||||
isEditing?: boolean;
|
||||
isEditing: boolean;
|
||||
isInView: boolean;
|
||||
width: number;
|
||||
height: number;
|
||||
@@ -257,7 +257,7 @@ export class PanelChrome extends PureComponent<Props, State> {
|
||||
return null;
|
||||
}
|
||||
|
||||
const PanelComponent = plugin.panel;
|
||||
const PanelComponent = plugin.panel!;
|
||||
const timeRange = data.timeRange || this.timeSrv.timeRange();
|
||||
const headerHeight = this.hasOverlayHeader() ? 0 : theme.panelHeaderHeight;
|
||||
const chromePadding = plugin.noPadding ? 0 : theme.panelPadding;
|
||||
|
||||
@@ -38,7 +38,7 @@ interface State {
|
||||
dataSource?: DataSourceApi;
|
||||
dataSourceItem: DataSourceSelectItem;
|
||||
dataSourceError?: string;
|
||||
helpContent: JSX.Element;
|
||||
helpContent: React.ReactNode;
|
||||
isLoadingHelp: boolean;
|
||||
isPickerOpen: boolean;
|
||||
isAddingMixed: boolean;
|
||||
@@ -96,7 +96,7 @@ export class QueriesTab extends PureComponent<Props, State> {
|
||||
this.setState({ data });
|
||||
}
|
||||
|
||||
findCurrentDataSource(dataSourceName: string = this.props.panel.datasource): DataSourceSelectItem {
|
||||
findCurrentDataSource(dataSourceName: string | null = this.props.panel.datasource): DataSourceSelectItem {
|
||||
return this.datasources.find(datasource => datasource.value === dataSourceName) || this.datasources[0];
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ interface Props {
|
||||
onMoveQuery: (query: DataQuery, direction: number) => void;
|
||||
onChange: (query: DataQuery) => void;
|
||||
dataSourceValue: string | null;
|
||||
inMixedMode: boolean;
|
||||
inMixedMode?: boolean;
|
||||
}
|
||||
|
||||
interface State {
|
||||
@@ -261,11 +261,12 @@ export class QueryEditorRow extends PureComponent<Props, State> {
|
||||
const { query, inMixedMode } = this.props;
|
||||
const { datasource } = this.state;
|
||||
const isDisabled = query.hide;
|
||||
|
||||
return (
|
||||
<QueryEditorRowTitle
|
||||
query={query}
|
||||
inMixedMode={inMixedMode}
|
||||
datasource={datasource}
|
||||
datasource={datasource!}
|
||||
disabled={isDisabled}
|
||||
onClick={e => this.onToggleEditMode(e, props)}
|
||||
collapsedText={!props.isOpen ? this.renderCollapsedText() : null}
|
||||
@@ -331,7 +332,7 @@ export interface AngularQueryComponentScope {
|
||||
events: Emitter;
|
||||
refresh: () => void;
|
||||
render: () => void;
|
||||
datasource: DataSourceApi;
|
||||
datasource: DataSourceApi | null;
|
||||
toggleEditorMode?: () => void;
|
||||
getCollapsedText?: () => string;
|
||||
range: TimeRange;
|
||||
|
||||
@@ -7,10 +7,10 @@ import { selectors } from '@grafana/e2e-selectors';
|
||||
interface QueryEditorRowTitleProps {
|
||||
query: DataQuery;
|
||||
datasource: DataSourceApi;
|
||||
inMixedMode: boolean;
|
||||
disabled: boolean;
|
||||
inMixedMode?: boolean;
|
||||
disabled?: boolean;
|
||||
onClick: (e: React.MouseEvent) => void;
|
||||
collapsedText: string;
|
||||
collapsedText: string | null;
|
||||
}
|
||||
|
||||
export const QueryEditorRowTitle: React.FC<QueryEditorRowTitleProps> = ({
|
||||
@@ -23,6 +23,7 @@ export const QueryEditorRowTitle: React.FC<QueryEditorRowTitleProps> = ({
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const styles = getQueryEditorRowTitleStyles(theme);
|
||||
|
||||
return (
|
||||
<HorizontalGroup align="center">
|
||||
<div className={styles.refId} aria-label={selectors.components.QueryEditorRow.title(query.refId)}>
|
||||
|
||||
@@ -123,7 +123,7 @@ export class ChangeTracker {
|
||||
}
|
||||
|
||||
// remove scopedVars
|
||||
panel.scopedVars = null;
|
||||
panel.scopedVars = undefined;
|
||||
|
||||
// ignore panel legend sort
|
||||
if (panel.legend) {
|
||||
|
||||
@@ -30,7 +30,7 @@ export class DashboardSrv {
|
||||
|
||||
onRemovePanel = (panelId: number) => {
|
||||
const dashboard = this.getCurrent();
|
||||
removePanel(dashboard, dashboard.getPanelById(panelId), true);
|
||||
removePanel(dashboard, dashboard.getPanelById(panelId)!, true);
|
||||
};
|
||||
|
||||
saveJSONDashboard(json: string) {
|
||||
|
||||
@@ -4,16 +4,7 @@ import _ from 'lodash';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import coreModule from 'app/core/core_module';
|
||||
// Types
|
||||
import {
|
||||
dateMath,
|
||||
DefaultTimeRange,
|
||||
TimeRange,
|
||||
RawTimeRange,
|
||||
TimeZone,
|
||||
toUtc,
|
||||
dateTime,
|
||||
isDateTime,
|
||||
} from '@grafana/data';
|
||||
import { dateMath, DefaultTimeRange, TimeRange, RawTimeRange, toUtc, dateTime, isDateTime } from '@grafana/data';
|
||||
import { ITimeoutService, ILocationService } from 'angular';
|
||||
import { ContextSrv } from 'app/core/services/context_srv';
|
||||
import { DashboardModel } from '../state/DashboardModel';
|
||||
@@ -28,8 +19,8 @@ export class TimeSrv {
|
||||
time: any;
|
||||
refreshTimer: any;
|
||||
refresh: any;
|
||||
oldRefresh: boolean;
|
||||
dashboard: Partial<DashboardModel>;
|
||||
oldRefresh: string | null | undefined;
|
||||
dashboard: DashboardModel;
|
||||
timeAtLoad: any;
|
||||
private autoRefreshBlocked: boolean;
|
||||
|
||||
@@ -56,7 +47,7 @@ export class TimeSrv {
|
||||
});
|
||||
}
|
||||
|
||||
init(dashboard: Partial<DashboardModel>) {
|
||||
init(dashboard: DashboardModel) {
|
||||
this.timer.cancelAll();
|
||||
|
||||
this.dashboard = dashboard;
|
||||
@@ -279,11 +270,11 @@ export class TimeSrv {
|
||||
to: isDateTime(this.time.to) ? dateTime(this.time.to) : this.time.to,
|
||||
};
|
||||
|
||||
const timezone: TimeZone = this.dashboard ? this.dashboard.getTimezone() : undefined;
|
||||
const timezone = this.dashboard ? this.dashboard.getTimezone() : undefined;
|
||||
|
||||
return {
|
||||
from: dateMath.parse(raw.from, false, timezone),
|
||||
to: dateMath.parse(raw.to, true, timezone),
|
||||
from: dateMath.parse(raw.from, false, timezone)!,
|
||||
to: dateMath.parse(raw.to, true, timezone)!,
|
||||
raw: raw,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -691,15 +691,15 @@ describe('DashboardModel', () => {
|
||||
expect(model.templating.list[1].tags).toEqual([
|
||||
{ text: 'Africa', selected: false },
|
||||
{
|
||||
text: 'America',
|
||||
selected: true,
|
||||
text: 'America',
|
||||
values: ['server-us-east', 'server-us-central', 'server-us-west'],
|
||||
valuesText: 'server-us-east + server-us-central + server-us-west',
|
||||
},
|
||||
{ text: 'Asia', selected: false },
|
||||
{
|
||||
text: 'Europe',
|
||||
selected: true,
|
||||
text: 'Europe',
|
||||
values: ['server-eu-east', 'server-eu-west'],
|
||||
valuesText: 'server-eu-east + server-eu-west',
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Libraries
|
||||
import _ from 'lodash';
|
||||
import _, { defaults } from 'lodash';
|
||||
// Utils
|
||||
import getFactors from 'app/core/utils/factors';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
@@ -557,8 +557,7 @@ export class DashboardMigrator {
|
||||
continue;
|
||||
}
|
||||
|
||||
const currentValue = currents[tag];
|
||||
newTags.push({ text: tag, selected: false, ...currentValue });
|
||||
newTags.push(defaults(currents[tag], { text: tag, selected: false }));
|
||||
}
|
||||
variable.tags = newTags;
|
||||
}
|
||||
@@ -621,7 +620,8 @@ export class DashboardMigrator {
|
||||
const rowGridHeight = getGridHeight(height);
|
||||
|
||||
const rowPanel: any = {};
|
||||
let rowPanelModel: PanelModel;
|
||||
let rowPanelModel: PanelModel | undefined;
|
||||
|
||||
if (showRows) {
|
||||
// add special row panel
|
||||
rowPanel.id = nextRowId;
|
||||
|
||||
@@ -72,7 +72,7 @@ export class DashboardModel {
|
||||
gnetId: any;
|
||||
panels: PanelModel[];
|
||||
panelInEdit?: PanelModel;
|
||||
panelInView: PanelModel;
|
||||
panelInView?: PanelModel;
|
||||
|
||||
// ------------------
|
||||
// not persisted
|
||||
@@ -158,7 +158,7 @@ export class DashboardModel {
|
||||
});
|
||||
}
|
||||
|
||||
private initMeta(meta: DashboardMeta) {
|
||||
private initMeta(meta?: DashboardMeta) {
|
||||
meta = meta || {};
|
||||
|
||||
meta.canShare = meta.canShare !== false;
|
||||
@@ -312,7 +312,7 @@ export class DashboardModel {
|
||||
}
|
||||
|
||||
exitPanelEditor() {
|
||||
this.panelInEdit.destroy();
|
||||
this.panelInEdit!.destroy();
|
||||
this.panelInEdit = undefined;
|
||||
}
|
||||
|
||||
@@ -352,7 +352,7 @@ export class DashboardModel {
|
||||
}
|
||||
}
|
||||
|
||||
getPanelById(id: number): PanelModel {
|
||||
getPanelById(id: number): PanelModel | null {
|
||||
if (this.panelInEdit && this.panelInEdit.id === id) {
|
||||
return this.panelInEdit;
|
||||
}
|
||||
@@ -362,14 +362,15 @@ export class DashboardModel {
|
||||
return panel;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
canEditPanel(panel?: PanelModel): boolean {
|
||||
canEditPanel(panel?: PanelModel | null): boolean | undefined | null {
|
||||
return this.meta.canEdit && panel && !panel.repeatPanelId;
|
||||
}
|
||||
|
||||
canEditPanelById(id: number): boolean {
|
||||
canEditPanelById(id: number): boolean | undefined | null {
|
||||
return this.canEditPanel(this.getPanelById(id));
|
||||
}
|
||||
|
||||
@@ -490,7 +491,8 @@ export class DashboardModel {
|
||||
|
||||
clone.repeatIteration = this.iteration;
|
||||
clone.repeatPanelId = sourcePanel.id;
|
||||
clone.repeat = null;
|
||||
clone.repeat = undefined;
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -642,7 +644,7 @@ export class DashboardModel {
|
||||
if (repeatedByRow) {
|
||||
panel.repeatedByRow = true;
|
||||
} else {
|
||||
panel.repeat = null;
|
||||
panel.repeat = undefined;
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
@@ -878,7 +880,7 @@ export class DashboardModel {
|
||||
this.events.on(event, callback);
|
||||
}
|
||||
|
||||
off<T>(event: AppEvent<T>, callback?: (payload?: T) => void) {
|
||||
off<T>(event: AppEvent<T>, callback: (payload?: T) => void) {
|
||||
this.events.off(event, callback);
|
||||
}
|
||||
|
||||
@@ -990,12 +992,12 @@ export class DashboardModel {
|
||||
});
|
||||
|
||||
// determine if more panels are displaying legends or not
|
||||
const onCount = panelsWithLegends.filter(panel => panel.legend.show).length;
|
||||
const onCount = panelsWithLegends.filter(panel => panel.legend!.show).length;
|
||||
const offCount = panelsWithLegends.length - onCount;
|
||||
const panelLegendsOn = onCount >= offCount;
|
||||
|
||||
for (const panel of panelsWithLegends) {
|
||||
panel.legend.show = !panelLegendsOn;
|
||||
panel.legend!.show = !panelLegendsOn;
|
||||
panel.render();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ export class PanelModel implements DataConfigSource {
|
||||
soloMode?: boolean;
|
||||
targets: DataQuery[];
|
||||
transformations?: DataTransformerConfig[];
|
||||
datasource: string;
|
||||
datasource: string | null;
|
||||
thresholds?: any;
|
||||
pluginVersion?: string;
|
||||
|
||||
@@ -352,7 +352,7 @@ export class PanelModel implements DataConfigSource {
|
||||
const pluginId = newPlugin.meta.id;
|
||||
const oldOptions: any = this.getOptionsToRemember();
|
||||
const oldPluginId = this.type;
|
||||
const wasAngular = !!this.plugin.angularPanelCtrl;
|
||||
const wasAngular = this.isAngularPlugin();
|
||||
|
||||
// remove panel type specific options
|
||||
for (const key of _.keys(this)) {
|
||||
@@ -458,7 +458,7 @@ export class PanelModel implements DataConfigSource {
|
||||
}
|
||||
|
||||
isAngularPlugin(): boolean {
|
||||
return this.plugin && !!this.plugin.angularPanelCtrl;
|
||||
return (this.plugin && this.plugin.angularPanelCtrl) !== undefined;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
||||
@@ -32,11 +32,11 @@ export interface QueryRunnerOptions<
|
||||
TQuery extends DataQuery = DataQuery,
|
||||
TOptions extends DataSourceJsonData = DataSourceJsonData
|
||||
> {
|
||||
datasource: string | DataSourceApi<TQuery, TOptions>;
|
||||
datasource: string | DataSourceApi<TQuery, TOptions> | null;
|
||||
queries: TQuery[];
|
||||
panelId: number;
|
||||
dashboardId?: number;
|
||||
timezone?: string;
|
||||
timezone: TimeZone;
|
||||
timeRange: TimeRange;
|
||||
timeInfo?: string; // String description of time range for display
|
||||
maxDataPoints: number;
|
||||
@@ -62,7 +62,6 @@ export class PanelQueryRunner {
|
||||
private subscription?: Unsubscribable;
|
||||
private lastResult?: PanelData;
|
||||
private dataConfigSource: DataConfigSource;
|
||||
private timeZone?: TimeZone;
|
||||
|
||||
constructor(dataConfigSource: DataConfigSource) {
|
||||
this.subject = new ReplaySubject(1);
|
||||
@@ -98,10 +97,9 @@ export class PanelQueryRunner {
|
||||
processedData = {
|
||||
...processedData,
|
||||
series: applyFieldOverrides({
|
||||
timeZone: this.timeZone,
|
||||
timeZone: data.request!.timezone,
|
||||
autoMinMax: true,
|
||||
data: processedData.series,
|
||||
getDataSourceSettingsByUid: getDatasourceSrv().getDataSourceSettingsByUid.bind(getDatasourceSrv()),
|
||||
...fieldConfig,
|
||||
}),
|
||||
};
|
||||
@@ -128,8 +126,6 @@ export class PanelQueryRunner {
|
||||
minInterval,
|
||||
} = options;
|
||||
|
||||
this.timeZone = timezone;
|
||||
|
||||
if (isSharedDashboardQuery(datasource)) {
|
||||
this.pipeToSubject(runSharedRequest(options));
|
||||
return;
|
||||
|
||||
@@ -33,7 +33,7 @@ export function emitDataRequestEvent(datasource: DataSourceApi) {
|
||||
panelId: data.request.panelId,
|
||||
dashboardId: data.request.dashboardId,
|
||||
dataSize: 0,
|
||||
duration: data.request.endTime - data.request.startTime,
|
||||
duration: data.request.endTime! - data.request.startTime,
|
||||
};
|
||||
|
||||
// enrich with dashboard info
|
||||
|
||||
@@ -89,7 +89,7 @@ async function fetchDashboard(
|
||||
case DashboardRouteInfo.Normal: {
|
||||
// for old db routes we redirect
|
||||
if (args.urlType === 'db') {
|
||||
redirectToNewUrl(args.urlSlug, dispatch, getState().location.path);
|
||||
redirectToNewUrl(args.urlSlug!, dispatch, getState().location.path);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ export function initDashboard(args: InitDashboardArgs): ThunkResult<void> {
|
||||
}
|
||||
|
||||
// template values service needs to initialize completely before the rest of the dashboard can load
|
||||
await dispatch(initVariablesTransaction(args.urlUid, dashboard));
|
||||
await dispatch(initVariablesTransaction(args.urlUid!, dashboard));
|
||||
|
||||
if (getState().templating.transaction.uid !== args.urlUid) {
|
||||
// if a previous dashboard has slow running variable queries the batch uid will be the new one
|
||||
|
||||
@@ -34,7 +34,7 @@ interface RunningQueryState {
|
||||
* This function should handle composing a PanelData from multiple responses
|
||||
*/
|
||||
export function processResponsePacket(packet: DataQueryResponse, state: RunningQueryState): RunningQueryState {
|
||||
const request = state.panelData.request;
|
||||
const request = state.panelData.request!;
|
||||
const packets: MapOfResponsePackets = {
|
||||
...state.packets,
|
||||
};
|
||||
@@ -48,8 +48,8 @@ export function processResponsePacket(packet: DataQueryResponse, state: RunningQ
|
||||
const range = { ...request.range };
|
||||
const timeRange = isString(range.raw.from)
|
||||
? {
|
||||
from: dateMath.parse(range.raw.from, false),
|
||||
to: dateMath.parse(range.raw.to, true),
|
||||
from: dateMath.parse(range.raw.from, false)!,
|
||||
to: dateMath.parse(range.raw.to, true)!,
|
||||
raw: range.raw,
|
||||
}
|
||||
: range;
|
||||
|
||||
@@ -59,6 +59,7 @@ describe('getPanelMenu', () => {
|
||||
"type": "submenu",
|
||||
},
|
||||
Object {
|
||||
"text": "",
|
||||
"type": "divider",
|
||||
},
|
||||
Object {
|
||||
@@ -82,63 +83,64 @@ describe('getPanelMenu', () => {
|
||||
|
||||
const menuItems = getPanelMenu(dashboard, panel, angularComponent);
|
||||
expect(menuItems).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"iconClassName": "eye",
|
||||
"onClick": [Function],
|
||||
"shortcut": "v",
|
||||
"text": "View",
|
||||
},
|
||||
Object {
|
||||
"iconClassName": "edit",
|
||||
"onClick": [Function],
|
||||
"shortcut": "e",
|
||||
"text": "Edit",
|
||||
},
|
||||
Object {
|
||||
"iconClassName": "share-alt",
|
||||
"onClick": [Function],
|
||||
"shortcut": "p s",
|
||||
"text": "Share",
|
||||
},
|
||||
Object {
|
||||
"iconClassName": "info-circle",
|
||||
"onClick": [Function],
|
||||
"shortcut": "i",
|
||||
"subMenu": Array [
|
||||
Object {
|
||||
"onClick": [Function],
|
||||
"text": "Panel JSON",
|
||||
},
|
||||
],
|
||||
"text": "Inspect",
|
||||
"type": "submenu",
|
||||
},
|
||||
Object {
|
||||
"iconClassName": "cube",
|
||||
"onClick": [Function],
|
||||
"subMenu": Array [
|
||||
Object {
|
||||
"href": undefined,
|
||||
"onClick": [Function],
|
||||
"shortcut": "p l",
|
||||
"text": "Toggle legend",
|
||||
},
|
||||
],
|
||||
"text": "More...",
|
||||
"type": "submenu",
|
||||
},
|
||||
Object {
|
||||
"type": "divider",
|
||||
},
|
||||
Object {
|
||||
"iconClassName": "trash-alt",
|
||||
"onClick": [Function],
|
||||
"shortcut": "p r",
|
||||
"text": "Remove",
|
||||
},
|
||||
]
|
||||
`);
|
||||
Array [
|
||||
Object {
|
||||
"iconClassName": "eye",
|
||||
"onClick": [Function],
|
||||
"shortcut": "v",
|
||||
"text": "View",
|
||||
},
|
||||
Object {
|
||||
"iconClassName": "edit",
|
||||
"onClick": [Function],
|
||||
"shortcut": "e",
|
||||
"text": "Edit",
|
||||
},
|
||||
Object {
|
||||
"iconClassName": "share-alt",
|
||||
"onClick": [Function],
|
||||
"shortcut": "p s",
|
||||
"text": "Share",
|
||||
},
|
||||
Object {
|
||||
"iconClassName": "info-circle",
|
||||
"onClick": [Function],
|
||||
"shortcut": "i",
|
||||
"subMenu": Array [
|
||||
Object {
|
||||
"onClick": [Function],
|
||||
"text": "Panel JSON",
|
||||
},
|
||||
],
|
||||
"text": "Inspect",
|
||||
"type": "submenu",
|
||||
},
|
||||
Object {
|
||||
"iconClassName": "cube",
|
||||
"onClick": [Function],
|
||||
"subMenu": Array [
|
||||
Object {
|
||||
"href": undefined,
|
||||
"onClick": [Function],
|
||||
"shortcut": "p l",
|
||||
"text": "Toggle legend",
|
||||
},
|
||||
],
|
||||
"text": "More...",
|
||||
"type": "submenu",
|
||||
},
|
||||
Object {
|
||||
"text": "",
|
||||
"type": "divider",
|
||||
},
|
||||
Object {
|
||||
"iconClassName": "trash-alt",
|
||||
"onClick": [Function],
|
||||
"shortcut": "p r",
|
||||
"text": "Remove",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -46,8 +46,6 @@ export function getPanelMenu(
|
||||
};
|
||||
|
||||
const onInspectPanel = (tab?: string) => {
|
||||
event.preventDefault();
|
||||
|
||||
getLocationSrv().update({
|
||||
partial: true,
|
||||
query: {
|
||||
@@ -198,7 +196,7 @@ export function getPanelMenu(
|
||||
}
|
||||
|
||||
if (dashboard.canEditPanel(panel) && !panel.isEditing) {
|
||||
menu.push({ type: 'divider' });
|
||||
menu.push({ type: 'divider', text: '' });
|
||||
|
||||
menu.push({
|
||||
text: 'Remove',
|
||||
|
||||
@@ -25,8 +25,8 @@ import { ShareModal } from 'app/features/dashboard/components/ShareModal';
|
||||
export const removePanel = (dashboard: DashboardModel, panel: PanelModel, ask: boolean) => {
|
||||
// confirm deletion
|
||||
if (ask !== false) {
|
||||
const text2 = panel.alert ? 'Panel includes an alert rule, removing panel will also remove alert rule' : null;
|
||||
const confirmText = panel.alert ? 'YES' : null;
|
||||
const text2 = panel.alert ? 'Panel includes an alert rule, removing panel will also remove alert rule' : undefined;
|
||||
const confirmText = panel.alert ? 'YES' : undefined;
|
||||
|
||||
appEvents.emit(CoreEvents.showConfirmModal, {
|
||||
title: 'Remove Panel',
|
||||
@@ -92,11 +92,11 @@ export function applyPanelTimeOverrides(panel: PanelModel, timeRange: TimeRange)
|
||||
}
|
||||
|
||||
if (_isString(timeRange.raw.from)) {
|
||||
const timeFromDate = dateMath.parse(timeFromInfo.from);
|
||||
const timeFromDate = dateMath.parse(timeFromInfo.from)!;
|
||||
newTimeData.timeInfo = timeFromInfo.display;
|
||||
newTimeData.timeRange = {
|
||||
from: timeFromDate,
|
||||
to: dateMath.parse(timeFromInfo.to),
|
||||
to: dateMath.parse(timeFromInfo.to)!,
|
||||
raw: {
|
||||
from: timeFromInfo.from,
|
||||
to: timeFromInfo.to,
|
||||
@@ -115,8 +115,8 @@ export function applyPanelTimeOverrides(panel: PanelModel, timeRange: TimeRange)
|
||||
|
||||
const timeShift = '-' + timeShiftInterpolated;
|
||||
newTimeData.timeInfo += ' timeshift ' + timeShift;
|
||||
const from = dateMath.parseDateMath(timeShift, newTimeData.timeRange.from, false);
|
||||
const to = dateMath.parseDateMath(timeShift, newTimeData.timeRange.to, true);
|
||||
const from = dateMath.parseDateMath(timeShift, newTimeData.timeRange.from, false)!;
|
||||
const to = dateMath.parseDateMath(timeShift, newTimeData.timeRange.to, true)!;
|
||||
|
||||
newTimeData.timeRange = {
|
||||
from,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { GenericDataSourcePlugin } from '../settings/PluginSettings';
|
||||
export function buildNavModel(dataSource: DataSourceSettings, plugin: GenericDataSourcePlugin): NavModelItem {
|
||||
const pluginMeta = plugin.meta;
|
||||
|
||||
const navModel = {
|
||||
const navModel: NavModelItem = {
|
||||
img: pluginMeta.info.logos.large,
|
||||
id: 'datasource-' + dataSource.id,
|
||||
subTitle: `Type: ${pluginMeta.name}`,
|
||||
@@ -25,7 +25,7 @@ export function buildNavModel(dataSource: DataSourceSettings, plugin: GenericDat
|
||||
|
||||
if (plugin.configPages) {
|
||||
for (const page of plugin.configPages) {
|
||||
navModel.children.push({
|
||||
navModel.children!.push({
|
||||
active: false,
|
||||
text: page.title,
|
||||
icon: page.icon,
|
||||
@@ -36,7 +36,7 @@ export function buildNavModel(dataSource: DataSourceSettings, plugin: GenericDat
|
||||
}
|
||||
|
||||
if (pluginMeta.includes && hasDashboards(pluginMeta.includes)) {
|
||||
navModel.children.push({
|
||||
navModel.children!.push({
|
||||
active: false,
|
||||
icon: 'apps',
|
||||
id: `datasource-dashboards-${dataSource.id}`,
|
||||
@@ -46,7 +46,7 @@ export function buildNavModel(dataSource: DataSourceSettings, plugin: GenericDat
|
||||
}
|
||||
|
||||
if (config.licenseInfo.hasLicense) {
|
||||
navModel.children.push({
|
||||
navModel.children!.push({
|
||||
active: false,
|
||||
icon: 'lock',
|
||||
id: `datasource-permissions-${dataSource.id}`,
|
||||
@@ -55,7 +55,7 @@ export function buildNavModel(dataSource: DataSourceSettings, plugin: GenericDat
|
||||
});
|
||||
|
||||
if (config.featureToggles.datasourceInsights) {
|
||||
navModel.children.push({
|
||||
navModel.children!.push({
|
||||
active: false,
|
||||
icon: 'info-circle',
|
||||
id: `datasource-insights-${dataSource.id}`,
|
||||
|
||||
@@ -17,7 +17,7 @@ export const getDataSourcePlugins = (state: DataSourcesState) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const getDataSource = (state: DataSourcesState, dataSourceId: UrlQueryValue): DataSourceSettings | null => {
|
||||
export const getDataSource = (state: DataSourcesState, dataSourceId: UrlQueryValue): DataSourceSettings => {
|
||||
if (state.dataSource.id === parseInt(dataSourceId as string, 10)) {
|
||||
return state.dataSource;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ describe('createResetHandler', () => {
|
||||
createResetHandler(ctrl, field)(event);
|
||||
expect(ctrl).toEqual({
|
||||
current: {
|
||||
[field]: null,
|
||||
[field]: undefined,
|
||||
secureJsonData: {
|
||||
[field]: '',
|
||||
},
|
||||
|
||||
@@ -31,7 +31,7 @@ export const createResetHandler = (ctrl: Ctrl, field: PasswordFieldEnum) => (
|
||||
) => {
|
||||
event.preventDefault();
|
||||
// Reset also normal plain text password to remove it and only save it in secureJsonData.
|
||||
ctrl.current[field] = null;
|
||||
ctrl.current[field] = undefined;
|
||||
ctrl.current.secureJsonFields[field] = false;
|
||||
ctrl.current.secureJsonData = ctrl.current.secureJsonData || {};
|
||||
ctrl.current.secureJsonData[field] = '';
|
||||
|
||||
@@ -86,7 +86,7 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
|
||||
export interface ExploreProps {
|
||||
changeSize: typeof changeSize;
|
||||
datasourceInstance: DataSourceApi;
|
||||
datasourceInstance: DataSourceApi | null;
|
||||
datasourceMissing: boolean;
|
||||
exploreId: ExploreId;
|
||||
initializeExplore: typeof initializeExplore;
|
||||
@@ -109,7 +109,7 @@ export interface ExploreProps {
|
||||
isLive: boolean;
|
||||
syncedTimes: boolean;
|
||||
updateTimeRange: typeof updateTimeRange;
|
||||
graphResult?: GraphSeriesXY[];
|
||||
graphResult?: GraphSeriesXY[] | null;
|
||||
loading?: boolean;
|
||||
absoluteRange: AbsoluteTimeRange;
|
||||
showingGraph?: boolean;
|
||||
|
||||
@@ -40,7 +40,7 @@ const getStyles = (theme: GrafanaTheme) => ({
|
||||
});
|
||||
|
||||
interface Props extends Themeable {
|
||||
series?: GraphSeriesXY[];
|
||||
series?: GraphSeriesXY[] | null;
|
||||
width: number;
|
||||
absoluteRange: AbsoluteTimeRange;
|
||||
loading?: boolean;
|
||||
|
||||
@@ -65,9 +65,9 @@ interface StateProps {
|
||||
hasLiveOption: boolean;
|
||||
isLive: boolean;
|
||||
isPaused: boolean;
|
||||
originPanelId?: number;
|
||||
originPanelId?: number | null;
|
||||
queries: DataQuery[];
|
||||
datasourceLoading?: boolean;
|
||||
datasourceLoading?: boolean | null;
|
||||
containerWidth: number;
|
||||
datasourceName?: string;
|
||||
}
|
||||
@@ -130,7 +130,7 @@ export class UnConnectedExploreToolbar extends PureComponent<Props> {
|
||||
|
||||
if (withChanges) {
|
||||
this.props.setDashboardQueriesToUpdateOnLoad({
|
||||
panelId: originPanelId,
|
||||
panelId: originPanelId!,
|
||||
queries: this.cleanQueries(queries),
|
||||
});
|
||||
}
|
||||
@@ -235,7 +235,7 @@ export class UnConnectedExploreToolbar extends PureComponent<Props> {
|
||||
onChange={this.onChangeDatasource}
|
||||
datasources={getExploreDatasources()}
|
||||
current={this.getSelectedDatasource()}
|
||||
showLoading={datasourceLoading}
|
||||
showLoading={datasourceLoading === true}
|
||||
hideTextValue={showSmallDataSourcePicker}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -129,7 +129,7 @@ export function LiveTailButton(props: LiveTailButtonProps) {
|
||||
[styles.isPaused]: isLive && isPaused,
|
||||
})}
|
||||
icon={!isLive ? 'play' : 'pause'}
|
||||
iconClassName={isLive && 'icon-brand-gradient'}
|
||||
iconClassName={isLive ? 'icon-brand-gradient' : undefined}
|
||||
onClick={onClickMain}
|
||||
title={'\xa0Live'}
|
||||
/>
|
||||
|
||||
@@ -3,14 +3,12 @@ import React, { PureComponent } from 'react';
|
||||
|
||||
// Services
|
||||
import { getAngularLoader, AngularComponent } from '@grafana/runtime';
|
||||
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
|
||||
// Types
|
||||
import { Emitter } from 'app/core/utils/emitter';
|
||||
import { DataQuery } from '@grafana/data';
|
||||
import { TimeRange } from '@grafana/data';
|
||||
import 'app/features/plugins/plugin_loader';
|
||||
import { dateTime } from '@grafana/data';
|
||||
|
||||
interface QueryEditorProps {
|
||||
error?: any;
|
||||
@@ -33,8 +31,7 @@ export default class QueryEditor extends PureComponent<QueryEditorProps, any> {
|
||||
return;
|
||||
}
|
||||
|
||||
const { datasource, initialQuery, exploreEvents, range } = this.props;
|
||||
this.initTimeSrv(range);
|
||||
const { datasource, initialQuery, exploreEvents } = this.props;
|
||||
|
||||
const loader = getAngularLoader();
|
||||
const template = '<plugin-component type="query-ctrl"> </plugin-component>';
|
||||
@@ -92,19 +89,6 @@ export default class QueryEditor extends PureComponent<QueryEditorProps, any> {
|
||||
}
|
||||
}
|
||||
|
||||
initTimeSrv(range: TimeRange) {
|
||||
const timeSrv = getTimeSrv();
|
||||
timeSrv.init({
|
||||
time: {
|
||||
from: dateTime(range.from),
|
||||
to: dateTime(range.to),
|
||||
},
|
||||
refresh: false,
|
||||
getTimezone: () => 'utc',
|
||||
timeRangeUpdated: () => console.log('refreshDashboard!'),
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className="gf-form-query" ref={element => (this.element = element)} style={{ width: '100%' }} />;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export interface Props {
|
||||
exploreId: ExploreId;
|
||||
height: number;
|
||||
onChangeSortOrder: (sortOrder: SortOrder) => void;
|
||||
onSelectDatasourceFilters: (value: SelectableValue[] | null) => void;
|
||||
onSelectDatasourceFilters: (value: SelectableValue[]) => void;
|
||||
}
|
||||
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme, height: number) => {
|
||||
|
||||
@@ -25,7 +25,7 @@ export interface Props {
|
||||
datasourceFilters: SelectableValue[] | null;
|
||||
exploreId: ExploreId;
|
||||
onChangeSortOrder: (sortOrder: SortOrder) => void;
|
||||
onSelectDatasourceFilters: (value: SelectableValue[] | null) => void;
|
||||
onSelectDatasourceFilters: (value: SelectableValue[]) => void;
|
||||
}
|
||||
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
|
||||
@@ -43,7 +43,7 @@ export function RunButton(props: Props) {
|
||||
'btn--radius-right-0': showDropdown,
|
||||
})}
|
||||
icon={loading ? 'fa fa-spinner' : 'sync'}
|
||||
iconClassName={loading && ' fa-spin run-icon'}
|
||||
iconClassName={loading ? ' fa-spin run-icon' : undefined}
|
||||
aria-label={selectors.pages.Explore.General.runButton}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -47,13 +47,13 @@ export class TableContainer extends PureComponent<TableContainerProps> {
|
||||
const tableWidth = width - config.theme.panelPadding * 2 - PANEL_BORDER;
|
||||
const hasTableResult = tableResult?.length;
|
||||
|
||||
if (hasTableResult) {
|
||||
if (tableResult && tableResult.length) {
|
||||
// Bit of code smell here. We need to add links here to the frame modifying the frame on every render.
|
||||
// Should work fine in essence but still not the ideal way to pass props. In logs container we do this
|
||||
// differently and sidestep this getLinks API on a dataframe
|
||||
for (const field of tableResult.fields) {
|
||||
field.getLinks = (config: ValueLinkConfig) => {
|
||||
return getFieldLinksForExplore(field, config.valueRowIndex, splitOpen, range);
|
||||
return getFieldLinksForExplore(field, config.valueRowIndex!, splitOpen, range);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ export function TraceView(props: Props) {
|
||||
} as ThemeOptions),
|
||||
[theme]
|
||||
);
|
||||
|
||||
const traceTimeline: TTraceTimeline = useMemo(
|
||||
() => ({
|
||||
childrenHiddenIDs,
|
||||
@@ -70,11 +71,15 @@ export function TraceView(props: Props) {
|
||||
hoverIndentGuideIds,
|
||||
shouldScrollToFirstUiFindMatch: false,
|
||||
spanNameColumnWidth,
|
||||
traceID: traceProp.traceID,
|
||||
traceID: traceProp?.traceID,
|
||||
}),
|
||||
[childrenHiddenIDs, detailStates, hoverIndentGuideIds, spanNameColumnWidth, traceProp.traceID]
|
||||
[childrenHiddenIDs, detailStates, hoverIndentGuideIds, spanNameColumnWidth, traceProp?.traceID]
|
||||
);
|
||||
|
||||
if (!traceProp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ThemeProvider value={traceTheme}>
|
||||
<UIElementsContext.Provider value={UIElements}>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { TraceSpan } from '@grafana/data';
|
||||
*/
|
||||
export function useSearch(spans?: TraceSpan[]) {
|
||||
const [search, setSearch] = useState('');
|
||||
const spanFindMatches: Set<string> | undefined = useMemo(() => {
|
||||
const spanFindMatches: Set<string> | undefined | null = useMemo(() => {
|
||||
return search && spans ? filterSpans(search, spans) : undefined;
|
||||
}, [search, spans]);
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ export interface InitializeExplorePayload {
|
||||
range: TimeRange;
|
||||
mode: ExploreMode;
|
||||
ui: ExploreUIState;
|
||||
originPanelId: number;
|
||||
originPanelId?: number | null;
|
||||
}
|
||||
|
||||
export interface LoadDatasourceMissingPayload {
|
||||
|
||||
@@ -93,7 +93,7 @@ import { getShiftedTimeRange } from 'app/core/utils/timePicker';
|
||||
import { updateLocation } from '../../../core/actions';
|
||||
import { getTimeSrv, TimeSrv } from '../../dashboard/services/TimeSrv';
|
||||
import { preProcessPanelData, runRequest } from '../../dashboard/state/runRequest';
|
||||
import { PanelModel } from 'app/features/dashboard/state';
|
||||
import { PanelModel, DashboardModel } from 'app/features/dashboard/state';
|
||||
import { getExploreDatasources } from './selectors';
|
||||
import { serializeStateToUrlParam } from '@grafana/data/src/utils/url';
|
||||
|
||||
@@ -295,7 +295,7 @@ export function initializeExplore(
|
||||
containerWidth: number,
|
||||
eventBridge: Emitter,
|
||||
ui: ExploreUIState,
|
||||
originPanelId: number
|
||||
originPanelId?: number | null
|
||||
): ThunkResult<void> {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch(loadExploreDatasourcesAndSetDatasource(exploreId, datasourceName));
|
||||
@@ -348,7 +348,7 @@ export const loadDatasourceReady = (
|
||||
export const importQueries = (
|
||||
exploreId: ExploreId,
|
||||
queries: DataQuery[],
|
||||
sourceDataSource: DataSourceApi | undefined,
|
||||
sourceDataSource: DataSourceApi | undefined | null,
|
||||
targetDataSource: DataSourceApi
|
||||
): ThunkResult<void> => {
|
||||
return async dispatch => {
|
||||
@@ -455,6 +455,10 @@ export const runQueries = (exploreId: ExploreId): ThunkResult<void> => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!datasourceInstance) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Some datasource's query builders allow per-query interval limits,
|
||||
// but we're using the datasource interval limit for now
|
||||
const minInterval = datasourceInstance.interval;
|
||||
@@ -584,7 +588,7 @@ export const stateSave = (): ThunkResult<void> => {
|
||||
const replace = left && left.urlReplaced === false;
|
||||
const urlStates: { [index: string]: string } = { orgId };
|
||||
const leftUrlState: ExploreUrlState = {
|
||||
datasource: left.datasourceInstance.name,
|
||||
datasource: left.datasourceInstance!.name,
|
||||
queries: left.queries.map(clearQueryKeys),
|
||||
range: toRawTimeRange(left.range),
|
||||
mode: left.mode,
|
||||
@@ -598,7 +602,7 @@ export const stateSave = (): ThunkResult<void> => {
|
||||
urlStates.left = serializeStateToUrlParam(leftUrlState, true);
|
||||
if (split) {
|
||||
const rightUrlState: ExploreUrlState = {
|
||||
datasource: right.datasourceInstance.name,
|
||||
datasource: right.datasourceInstance!.name,
|
||||
queries: right.queries.map(clearQueryKeys),
|
||||
range: toRawTimeRange(right.range),
|
||||
mode: right.mode,
|
||||
@@ -646,12 +650,13 @@ export const updateTime = (config: {
|
||||
const range = getTimeRange(timeZone, rawRange);
|
||||
const absoluteRange: AbsoluteTimeRange = { from: range.from.valueOf(), to: range.to.valueOf() };
|
||||
|
||||
getTimeSrv().init({
|
||||
time: range.raw,
|
||||
refresh: false,
|
||||
getTimezone: () => timeZone,
|
||||
timeRangeUpdated: (): any => undefined,
|
||||
});
|
||||
getTimeSrv().init(
|
||||
new DashboardModel({
|
||||
time: range.raw,
|
||||
refresh: false,
|
||||
timeZone,
|
||||
})
|
||||
);
|
||||
|
||||
dispatch(changeRangeAction({ exploreId, range, absoluteRange }));
|
||||
};
|
||||
@@ -716,9 +721,9 @@ export function splitOpen<T extends DataQuery = any>(options?: { datasourceUid:
|
||||
|
||||
if (options) {
|
||||
rightState.queries = [];
|
||||
rightState.graphResult = undefined;
|
||||
rightState.logsResult = undefined;
|
||||
rightState.tableResult = undefined;
|
||||
rightState.graphResult = null;
|
||||
rightState.logsResult = null;
|
||||
rightState.tableResult = null;
|
||||
rightState.queryKeys = [];
|
||||
urlState.queries = [];
|
||||
rightState.urlState = urlState;
|
||||
@@ -733,7 +738,7 @@ export function splitOpen<T extends DataQuery = any>(options?: { datasourceUid:
|
||||
];
|
||||
|
||||
const dataSourceSettings = getDatasourceSrv().getDataSourceSettingsByUid(options.datasourceUid);
|
||||
await dispatch(changeDatasource(ExploreId.right, dataSourceSettings.name));
|
||||
await dispatch(changeDatasource(ExploreId.right, dataSourceSettings!.name));
|
||||
await dispatch(setQueriesAction({ exploreId: ExploreId.right, queries }));
|
||||
await dispatch(runQueries(ExploreId.right));
|
||||
} else {
|
||||
@@ -827,12 +832,19 @@ export function refreshExplore(exploreId: ExploreId): ThunkResult<void> {
|
||||
}
|
||||
|
||||
const { urlState, update, containerWidth, eventBridge } = itemState;
|
||||
|
||||
if (!urlState) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { datasource, queries, range: urlRange, mode, ui, originPanelId } = urlState;
|
||||
const refreshQueries: DataQuery[] = [];
|
||||
|
||||
for (let index = 0; index < queries.length; index++) {
|
||||
const query = queries[index];
|
||||
refreshQueries.push(generateNewKeyAndAddRefIdIfMissing(query, refreshQueries, index));
|
||||
}
|
||||
|
||||
const timeZone = getTimeZone(getState().user);
|
||||
const range = getTimeRangeFromUrl(urlRange, timeZone);
|
||||
|
||||
@@ -884,7 +896,7 @@ export function refreshExplore(exploreId: ExploreId): ThunkResult<void> {
|
||||
export interface NavigateToExploreDependencies {
|
||||
getDataSourceSrv: () => DataSourceSrv;
|
||||
getTimeSrv: () => TimeSrv;
|
||||
getExploreUrl: (args: GetExploreUrlArguments) => Promise<string>;
|
||||
getExploreUrl: (args: GetExploreUrlArguments) => Promise<string | undefined>;
|
||||
openInNewWindow?: (url: string) => void;
|
||||
}
|
||||
|
||||
@@ -904,7 +916,7 @@ export const navigateToExplore = (
|
||||
timeSrv: getTimeSrv(),
|
||||
});
|
||||
|
||||
if (openInNewWindow) {
|
||||
if (openInNewWindow && path) {
|
||||
openInNewWindow(path);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -100,11 +100,11 @@ export const makeExploreItemState = (): ExploreItemState => ({
|
||||
from: null,
|
||||
to: null,
|
||||
raw: DEFAULT_RANGE,
|
||||
},
|
||||
} as any,
|
||||
absoluteRange: {
|
||||
from: null,
|
||||
to: null,
|
||||
},
|
||||
} as any,
|
||||
scanning: false,
|
||||
showingGraph: true,
|
||||
showingTable: true,
|
||||
@@ -129,7 +129,6 @@ export const makeExploreItemState = (): ExploreItemState => ({
|
||||
export const createEmptyQueryResponse = (): PanelData => ({
|
||||
state: LoadingState.NotStarted,
|
||||
series: [],
|
||||
error: null,
|
||||
timeRange: DefaultTimeRange,
|
||||
});
|
||||
|
||||
@@ -382,13 +381,14 @@ export const itemReducer = (state: ExploreItemState = makeExploreItemState(), ac
|
||||
const queriesAfterRemoval: DataQuery[] = [...queries.slice(0, index), ...queries.slice(index + 1)].map(query => {
|
||||
return { ...query, refId: '' };
|
||||
});
|
||||
|
||||
const nextQueries: DataQuery[] = [];
|
||||
|
||||
queriesAfterRemoval.forEach((query, i) => {
|
||||
nextQueries.push(generateNewKeyAndAddRefIdIfMissing(query, nextQueries, i));
|
||||
});
|
||||
|
||||
const nextQueryKeys: string[] = nextQueries.map(query => query.key);
|
||||
const nextQueryKeys: string[] = nextQueries.map(query => query.key!);
|
||||
|
||||
return {
|
||||
...state,
|
||||
@@ -544,6 +544,10 @@ export const processQueryResponse = (
|
||||
};
|
||||
}
|
||||
|
||||
if (!request) {
|
||||
return { ...state };
|
||||
}
|
||||
|
||||
const latency = request.endTime ? request.endTime - request.startTime : 0;
|
||||
const processor = new ResultProcessor(state, series, request.intervalMs, request.timezone as TimeZone);
|
||||
const graphResult = processor.getGraphResult();
|
||||
@@ -551,7 +555,7 @@ export const processQueryResponse = (
|
||||
const logsResult = processor.getLogsResult();
|
||||
|
||||
// Send legacy data to Angular editors
|
||||
if (state.datasourceInstance.components.QueryCtrl) {
|
||||
if (state.datasourceInstance?.components?.QueryCtrl) {
|
||||
const legacy = series.map(v => toLegacyResponseData(v));
|
||||
|
||||
state.eventBridge.emit(PanelEvents.dataReceived, legacy);
|
||||
@@ -575,6 +579,10 @@ export const updateChildRefreshState = (
|
||||
exploreId: ExploreId
|
||||
): ExploreItemState => {
|
||||
const path = payload.path || '';
|
||||
if (!payload.query) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const queryState = payload.query[exploreId] as string;
|
||||
if (!queryState) {
|
||||
return state;
|
||||
|
||||
@@ -34,7 +34,7 @@ export class DatasourceSrv implements DataSourceService {
|
||||
return Object.values(config.datasources).find(ds => ds.uid === uid);
|
||||
}
|
||||
|
||||
get(name?: string, scopedVars?: ScopedVars): Promise<DataSourceApi> {
|
||||
get(name?: string | null, scopedVars?: ScopedVars): Promise<DataSourceApi> {
|
||||
if (!name) {
|
||||
return this.get(config.defaultDatasource);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { DashboardQuery, SHARED_DASHBODARD_QUERY } from './types';
|
||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
import { LoadingState, DefaultTimeRange, DataQuery, PanelData, DataSourceApi } from '@grafana/data';
|
||||
|
||||
export function isSharedDashboardQuery(datasource: string | DataSourceApi) {
|
||||
export function isSharedDashboardQuery(datasource: string | DataSourceApi | null) {
|
||||
if (!datasource) {
|
||||
// default datasource
|
||||
return false;
|
||||
@@ -26,7 +26,7 @@ export function runSharedRequest(options: QueryRunnerOptions): Observable<PanelD
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const currentPanel = dashboard.getPanelById(options.panelId);
|
||||
const currentPanel = dashboard.getPanelById(options.panelId)!;
|
||||
const listenToPanel = dashboard.getPanelById(listenToPanelId);
|
||||
|
||||
if (!listenToPanel) {
|
||||
|
||||
@@ -908,6 +908,7 @@ const createElasticQuery = (): DataQueryRequest<ElasticsearchQuery> => {
|
||||
dashboardId: 0,
|
||||
interval: '',
|
||||
panelId: 0,
|
||||
intervalMs: 1,
|
||||
scopedVars: {},
|
||||
timezone: '',
|
||||
app: CoreApp.Dashboard,
|
||||
|
||||
@@ -100,11 +100,15 @@ export class GraphiteDatasource extends DataSourceApi<GraphiteQuery, GraphiteOpt
|
||||
return this.doGraphiteRequest(httpOptions).then(this.convertResponseToDataFrames);
|
||||
}
|
||||
|
||||
addTracingHeaders(httpOptions: { headers: any }, options: { dashboardId: any; panelId: any }) {
|
||||
addTracingHeaders(httpOptions: { headers: any }, options: { dashboardId?: number; panelId?: number }) {
|
||||
const proxyMode = !this.url.match(/^http/);
|
||||
if (proxyMode) {
|
||||
httpOptions.headers['X-Dashboard-Id'] = options.dashboardId;
|
||||
httpOptions.headers['X-Panel-Id'] = options.panelId;
|
||||
if (options.dashboardId) {
|
||||
httpOptions.headers['X-Dashboard-Id'] = options.dashboardId;
|
||||
}
|
||||
if (options.panelId) {
|
||||
httpOptions.headers['X-Panel-Id'] = options.panelId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,21 +52,15 @@ describe('JaegerQueryField', function() {
|
||||
);
|
||||
|
||||
// Simulating selection options. We need this as the function depends on the intermediate state of the component
|
||||
await wrapper
|
||||
.find(ButtonCascader)
|
||||
.props()
|
||||
.loadData([{ value: 'service1', label: 'service1' }]);
|
||||
await wrapper.find(ButtonCascader)!.props().loadData!([{ value: 'service1', label: 'service1' }]);
|
||||
|
||||
await wrapper
|
||||
.find(ButtonCascader)
|
||||
.props()
|
||||
.loadData([
|
||||
{ value: 'service1', label: 'service1' },
|
||||
{ value: 'op1', label: 'op1' },
|
||||
]);
|
||||
await wrapper.find(ButtonCascader)!.props().loadData!([
|
||||
{ value: 'service1', label: 'service1' },
|
||||
{ value: 'op1', label: 'op1' },
|
||||
]);
|
||||
|
||||
wrapper.update();
|
||||
expect(wrapper.find(ButtonCascader).props().options[0].children[1].children[0]).toEqual({
|
||||
expect(wrapper.find(ButtonCascader)!.props().options![0].children![1].children![0]).toEqual({
|
||||
label: 'rootOp [0.099 ms]',
|
||||
value: '12345',
|
||||
});
|
||||
|
||||
@@ -75,6 +75,7 @@ const defaultQuery: DataQueryRequest<JaegerQuery> = {
|
||||
requestId: '1',
|
||||
dashboardId: 0,
|
||||
interval: '0',
|
||||
intervalMs: 10,
|
||||
panelId: 0,
|
||||
scopedVars: {},
|
||||
range: {
|
||||
|
||||
@@ -22,6 +22,7 @@ const setup = (renderMethod: any, propOverrides?: object) => {
|
||||
requestId: '1',
|
||||
dashboardId: 1,
|
||||
interval: '1s',
|
||||
intervalMs: 1000,
|
||||
panelId: 1,
|
||||
range: {
|
||||
from: toUtc('2020-01-01', 'YYYY-MM-DD'),
|
||||
|
||||
@@ -24,6 +24,7 @@ exports[`LokiExploreQueryEditor should render component 1`] = `
|
||||
"app": "Grafana",
|
||||
"dashboardId": 1,
|
||||
"interval": "1s",
|
||||
"intervalMs": 1000,
|
||||
"panelId": 1,
|
||||
"range": Object {
|
||||
"from": "2020-01-01T00:00:00.000Z",
|
||||
|
||||
@@ -29,6 +29,7 @@ describe('DerivedFields', () => {
|
||||
|
||||
it('renders correctly when there are fields', async () => {
|
||||
let wrapper: any;
|
||||
//@ts-ignore
|
||||
await act(async () => {
|
||||
wrapper = await mount(<DerivedFields value={testValue} onChange={() => {}} />);
|
||||
});
|
||||
|
||||
@@ -73,7 +73,7 @@ describe('LokiDatasource', () => {
|
||||
range,
|
||||
};
|
||||
|
||||
const req = ds.createRangeQuery(target, options);
|
||||
const req = ds.createRangeQuery(target, options as any);
|
||||
expect(req.start).toBeDefined();
|
||||
expect(req.end).toBeDefined();
|
||||
expect(adjustIntervalSpy).toHaveBeenCalledWith(1000, expect.anything());
|
||||
|
||||
@@ -492,7 +492,7 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> {
|
||||
|
||||
const interpolatedExpr = this.templateSrv.replace(options.annotation.expr, {}, this.interpolateQueryExpr);
|
||||
const query = { refId: `annotation-${options.annotation.name}`, expr: interpolatedExpr };
|
||||
const { data } = await this.runRangeQuery(query, options).toPromise();
|
||||
const { data } = await this.runRangeQuery(query, options as any).toPromise();
|
||||
const annotations: AnnotationEvent[] = [];
|
||||
|
||||
for (const frame of data) {
|
||||
|
||||
@@ -18,6 +18,7 @@ const setup = (renderMethod: any, propOverrides?: object) => {
|
||||
request: {
|
||||
requestId: '1',
|
||||
dashboardId: 1,
|
||||
intervalMs: 1000,
|
||||
interval: '1s',
|
||||
panelId: 1,
|
||||
range: {
|
||||
|
||||
@@ -18,6 +18,7 @@ exports[`PromExploreQueryEditor should render component 1`] = `
|
||||
"app": "Grafana",
|
||||
"dashboardId": 1,
|
||||
"interval": "1s",
|
||||
"intervalMs": 1000,
|
||||
"panelId": 1,
|
||||
"range": Object {
|
||||
"from": "2020-01-01T00:00:00.000Z",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Libraries
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import defaults from 'lodash/defaults';
|
||||
// Services & Utils
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import {
|
||||
@@ -35,6 +34,7 @@ import { safeStringifyValue } from 'app/core/utils/explore';
|
||||
import templateSrv from 'app/features/templating/template_srv';
|
||||
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
import TableModel from 'app/core/table_model';
|
||||
import { defaults } from 'lodash';
|
||||
|
||||
export const ANNOTATION_QUERY_STEP_DEFAULT = '60s';
|
||||
|
||||
@@ -111,8 +111,8 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
||||
}
|
||||
}
|
||||
|
||||
_request(url: string, data: Record<string, string> | null, overrides?: Partial<BackendSrvRequest>) {
|
||||
const options: BackendSrvRequest = defaults(overrides || {}, {
|
||||
_request(url: string, data: Record<string, string> | null, overrides: Partial<BackendSrvRequest> = {}) {
|
||||
const options: BackendSrvRequest = defaults(overrides, {
|
||||
url: this.url + url,
|
||||
method: this.httpMethod,
|
||||
headers: {},
|
||||
@@ -128,7 +128,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
||||
.join('&');
|
||||
}
|
||||
} else {
|
||||
options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
options.headers!['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
options.data = data;
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
||||
}
|
||||
|
||||
if (this.basicAuth) {
|
||||
options.headers.Authorization = this.basicAuth;
|
||||
options.headers!.Authorization = this.basicAuth;
|
||||
}
|
||||
|
||||
return getBackendSrv().datasourceRequest(options);
|
||||
|
||||
@@ -68,7 +68,9 @@ export class GettingStarted extends PureComponent<PanelProps, State> {
|
||||
const { id } = this.props;
|
||||
const dashboard = getDashboardSrv().getCurrent();
|
||||
const panel = dashboard.getPanelById(id);
|
||||
dashboard.removePanel(panel);
|
||||
|
||||
dashboard.removePanel(panel!);
|
||||
|
||||
backendSrv
|
||||
.request({
|
||||
method: 'PUT',
|
||||
|
||||
@@ -19,10 +19,10 @@ export interface DashboardAclDTO {
|
||||
}
|
||||
|
||||
export interface DashboardAclUpdateDTO {
|
||||
userId: number;
|
||||
teamId: number;
|
||||
role: OrgRole;
|
||||
permission: PermissionLevel;
|
||||
userId?: number;
|
||||
teamId?: number;
|
||||
role?: OrgRole;
|
||||
permission?: PermissionLevel;
|
||||
}
|
||||
|
||||
export interface DashboardAcl {
|
||||
|
||||
@@ -80,7 +80,7 @@ export interface DashboardState {
|
||||
initPhase: DashboardInitPhase;
|
||||
isInitSlow: boolean;
|
||||
initError: DashboardInitError | null;
|
||||
permissions: DashboardAcl[] | null;
|
||||
permissions: DashboardAcl[];
|
||||
modifiedQueries: QueriesToUpdateOnDashboardLoad | null;
|
||||
panels: { [id: string]: PanelState };
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ export interface ExploreItemState {
|
||||
/**
|
||||
* Datasource instance that has been selected. Datasource-specific logic can be run on this object.
|
||||
*/
|
||||
datasourceInstance?: DataSourceApi;
|
||||
datasourceInstance?: DataSourceApi | null;
|
||||
/**
|
||||
* Current data source name or null if default
|
||||
*/
|
||||
@@ -157,7 +157,7 @@ export interface ExploreItemState {
|
||||
* Copy of the state of the URL which is in store.location.query. This is duplicated here so we can diff the two
|
||||
* after a change to see if we need to sync url state back to redux store (like on clicking Back in browser).
|
||||
*/
|
||||
urlState: ExploreUrlState;
|
||||
urlState: ExploreUrlState | null;
|
||||
|
||||
/**
|
||||
* Map of what changed between real url and local urlState so we can partially update just the things that are needed.
|
||||
@@ -187,7 +187,7 @@ export interface ExploreItemState {
|
||||
* Panel Id that is set if we come to explore from a penel. Used so we can get back to it and optionally modify the
|
||||
* query of that panel.
|
||||
*/
|
||||
originPanelId?: number;
|
||||
originPanelId?: number | null;
|
||||
}
|
||||
|
||||
export interface ExploreUpdateState {
|
||||
@@ -199,7 +199,7 @@ export interface ExploreUpdateState {
|
||||
}
|
||||
|
||||
export interface QueryOptions {
|
||||
minInterval: string;
|
||||
minInterval?: string;
|
||||
maxDataPoints?: number;
|
||||
liveStreaming?: boolean;
|
||||
showingGraph?: boolean;
|
||||
|
||||
Reference in New Issue
Block a user