Commit 49b5fc4b by Torkel Ödegaard Committed by GitHub

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
parent 9285595c
......@@ -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 };
}
......
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 (
_.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,8 +13,11 @@ 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);
......@@ -25,11 +28,7 @@ export const DashboardLinks: FC<Props> = ({ dashboard }) => {
}
const linkElement = (
<a
className="gf-form-label"
href={sanitizeUrl(linkInfo.href)}
target={link.targetBlank ? '_blank' : '_self'}
>
<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>
......@@ -42,6 +41,5 @@ export const DashboardLinks: FC<Props> = ({ dashboard }) => {
);
})}
</>
)
);
};
......@@ -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',
},
......
// 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 {
......@@ -129,6 +130,7 @@ describe('getPanelMenu', () => {
"type": "submenu",
},
Object {
"text": "",
"type": "divider",
},
Object {
......
......@@ -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({
getTimeSrv().init(
new DashboardModel({
time: range.raw,
refresh: false,
getTimezone: () => timeZone,
timeRangeUpdated: (): any => undefined,
});
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,13 +100,17 @@ 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) {
if (options.dashboardId) {
httpOptions.headers['X-Dashboard-Id'] = options.dashboardId;
}
if (options.panelId) {
httpOptions.headers['X-Panel-Id'] = options.panelId;
}
}
}
convertResponseToDataFrames = (result: any): DataQueryResponse => {
const data: DataFrame[] = [];
......
......@@ -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([
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",
......
// 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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment