diff --git a/packages/grafana-data/src/types/datasource.ts b/packages/grafana-data/src/types/datasource.ts index 02c5affd6fc..6d08b24545b 100644 --- a/packages/grafana-data/src/types/datasource.ts +++ b/packages/grafana-data/src/types/datasource.ts @@ -416,11 +416,9 @@ export interface DataQueryError { export interface DataQueryRequest { 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 { exploreMode?: ExploreMode; rangeRaw?: RawTimeRange; timeInfo?: string; // The query time description (blue text in the upper right) + panelId?: number; + dashboardId?: number; // Request Timing startTime: number; diff --git a/packages/grafana-data/src/types/panel.ts b/packages/grafana-data/src/types/panel.ts index c6e946c1d96..a0a89a2ecd4 100644 --- a/packages/grafana-data/src/types/panel.ts +++ b/packages/grafana-data/src/types/panel.ts @@ -164,7 +164,7 @@ export interface PanelOptionsEditorConfig) => void; shortcut?: string; diff --git a/public/app/core/components/Select/DataSourcePicker.tsx b/public/app/core/components/Select/DataSourcePicker.tsx index 98e465a8881..8cbeceb214f 100644 --- a/public/app/core/components/Select/DataSourcePicker.tsx +++ b/public/app/core/components/Select/DataSourcePicker.tsx @@ -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; diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index 2cb03cf8d5e..08b55081429 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -67,6 +67,7 @@ export interface GetExploreUrlArguments { datasourceSrv: DataSourceSrv; timeSrv: TimeSrv; } + export async function getExploreUrl(args: GetExploreUrlArguments): Promise { 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((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 }; } diff --git a/public/app/core/utils/query.ts b/public/app/core/utils/query.ts index 0b5c2740174..3d94afa6a6d 100644 --- a/public/app/core/utils/query.ts +++ b/public/app/core/utils/query.ts @@ -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[] { diff --git a/public/app/features/annotations/annotations_srv.ts b/public/app/features/annotations/annotations_srv.ts index 00a2fc525ab..bf6c9980aaa 100644 --- a/public/app/features/annotations/annotations_srv.ts +++ b/public/app/features/annotations/annotations_srv.ts @@ -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, diff --git a/public/app/features/dashboard/components/Inspector/InspectContent.tsx b/public/app/features/dashboard/components/Inspector/InspectContent.tsx index b93a1ea2437..7467d1114b8 100644 --- a/public/app/features/dashboard/components/Inspector/InspectContent.tsx +++ b/public/app/features/dashboard/components/Inspector/InspectContent.tsx @@ -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; diff --git a/public/app/features/dashboard/components/Inspector/PanelInspector.tsx b/public/app/features/dashboard/components/Inspector/PanelInspector.tsx index c3f24862bce..60d766fb565 100644 --- a/public/app/features/dashboard/components/Inspector/PanelInspector.tsx +++ b/public/app/features/dashboard/components/Inspector/PanelInspector.tsx @@ -15,7 +15,7 @@ import { updateLocation } from 'app/core/actions'; interface OwnProps { dashboard: DashboardModel; panel: PanelModel; - defaultTab: InspectTab; + defaultTab?: InspectTab; } export interface ConnectedProps { diff --git a/public/app/features/dashboard/components/SubMenu/Annotations.tsx b/public/app/features/dashboard/components/SubMenu/Annotations.tsx index 1125c1ebecc..ac7ec67c3ea 100644 --- a/public/app/features/dashboard/components/SubMenu/Annotations.tsx +++ b/public/app/features/dashboard/components/SubMenu/Annotations.tsx @@ -8,7 +8,7 @@ interface Props { } export const Annotations: FunctionComponent = ({ annotations, onAnnotationChanged }) => { - const [visibleAnnotations, setVisibleAnnotations] = useState([]); + const [visibleAnnotations, setVisibleAnnotations] = useState([]); useEffect(() => { setVisibleAnnotations(annotations.filter(annotation => annotation.hide !== true)); }, [annotations]); @@ -19,7 +19,7 @@ export const Annotations: FunctionComponent = ({ annotations, onAnnotatio return ( <> - {visibleAnnotations.map(annotation => { + {visibleAnnotations.map((annotation: any) => { return (
= ({ 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 ; - } + if (link.type === 'dashboards') { + return ; + } - const linkElement = ( - - - {sanitize(linkInfo.title)} - - ); + const linkElement = ( + + + {sanitize(linkInfo.title)} + + ); - return ( -
- {link.tooltip ? {linkElement} : linkElement} -
- ); - })} - - ) + return ( +
+ {link.tooltip ? {linkElement} : linkElement} +
+ ); + })} + ); }; diff --git a/public/app/features/dashboard/components/TransformationsEditor/TransformationEditor.tsx b/public/app/features/dashboard/components/TransformationsEditor/TransformationEditor.tsx index 63ce7de782c..272a78d4067 100644 --- a/public/app/features/dashboard/components/TransformationsEditor/TransformationEditor.tsx +++ b/public/app/features/dashboard/components/TransformationsEditor/TransformationEditor.tsx @@ -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
Transformation output data
-
- -
+
{output && }
)} diff --git a/public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx b/public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx index 816187e775a..4784d53c847 100644 --- a/public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx +++ b/public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx @@ -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[]; diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 2f78c8a8b4b..84ac07064b4 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -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 { 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; diff --git a/public/app/features/dashboard/panel_editor/QueriesTab.tsx b/public/app/features/dashboard/panel_editor/QueriesTab.tsx index b1f53ae8c5e..6e2859cc3a6 100644 --- a/public/app/features/dashboard/panel_editor/QueriesTab.tsx +++ b/public/app/features/dashboard/panel_editor/QueriesTab.tsx @@ -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 { 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]; } diff --git a/public/app/features/dashboard/panel_editor/QueryEditorRow.tsx b/public/app/features/dashboard/panel_editor/QueryEditorRow.tsx index ec103ffdc3e..775a1d4c5a4 100644 --- a/public/app/features/dashboard/panel_editor/QueryEditorRow.tsx +++ b/public/app/features/dashboard/panel_editor/QueryEditorRow.tsx @@ -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 { const { query, inMixedMode } = this.props; const { datasource } = this.state; const isDisabled = query.hide; + return ( 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; diff --git a/public/app/features/dashboard/panel_editor/QueryEditorRowTitle.tsx b/public/app/features/dashboard/panel_editor/QueryEditorRowTitle.tsx index 515749c5fc8..0c9f172bca7 100644 --- a/public/app/features/dashboard/panel_editor/QueryEditorRowTitle.tsx +++ b/public/app/features/dashboard/panel_editor/QueryEditorRowTitle.tsx @@ -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 = ({ @@ -23,6 +23,7 @@ export const QueryEditorRowTitle: React.FC = ({ }) => { const theme = useTheme(); const styles = getQueryEditorRowTitleStyles(theme); + return (
diff --git a/public/app/features/dashboard/services/ChangeTracker.ts b/public/app/features/dashboard/services/ChangeTracker.ts index 238c42bdcd5..cc0635b55d2 100644 --- a/public/app/features/dashboard/services/ChangeTracker.ts +++ b/public/app/features/dashboard/services/ChangeTracker.ts @@ -123,7 +123,7 @@ export class ChangeTracker { } // remove scopedVars - panel.scopedVars = null; + panel.scopedVars = undefined; // ignore panel legend sort if (panel.legend) { diff --git a/public/app/features/dashboard/services/DashboardSrv.ts b/public/app/features/dashboard/services/DashboardSrv.ts index ed3fad95641..e86e686d170 100644 --- a/public/app/features/dashboard/services/DashboardSrv.ts +++ b/public/app/features/dashboard/services/DashboardSrv.ts @@ -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) { diff --git a/public/app/features/dashboard/services/TimeSrv.ts b/public/app/features/dashboard/services/TimeSrv.ts index c185879e1d2..fe62f45615a 100644 --- a/public/app/features/dashboard/services/TimeSrv.ts +++ b/public/app/features/dashboard/services/TimeSrv.ts @@ -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; + oldRefresh: string | null | undefined; + dashboard: DashboardModel; timeAtLoad: any; private autoRefreshBlocked: boolean; @@ -56,7 +47,7 @@ export class TimeSrv { }); } - init(dashboard: Partial) { + 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, }; } diff --git a/public/app/features/dashboard/state/DashboardMigrator.test.ts b/public/app/features/dashboard/state/DashboardMigrator.test.ts index b9634ad7dd0..18307305f96 100644 --- a/public/app/features/dashboard/state/DashboardMigrator.test.ts +++ b/public/app/features/dashboard/state/DashboardMigrator.test.ts @@ -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', }, diff --git a/public/app/features/dashboard/state/DashboardMigrator.ts b/public/app/features/dashboard/state/DashboardMigrator.ts index b29e320863e..5517ae5bcc3 100644 --- a/public/app/features/dashboard/state/DashboardMigrator.ts +++ b/public/app/features/dashboard/state/DashboardMigrator.ts @@ -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; diff --git a/public/app/features/dashboard/state/DashboardModel.ts b/public/app/features/dashboard/state/DashboardModel.ts index e98eba0e04d..e19bafee286 100644 --- a/public/app/features/dashboard/state/DashboardModel.ts +++ b/public/app/features/dashboard/state/DashboardModel.ts @@ -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(event: AppEvent, callback?: (payload?: T) => void) { + off(event: AppEvent, 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(); } } diff --git a/public/app/features/dashboard/state/PanelModel.ts b/public/app/features/dashboard/state/PanelModel.ts index ee7f5d189a9..1a3a0afedfb 100644 --- a/public/app/features/dashboard/state/PanelModel.ts +++ b/public/app/features/dashboard/state/PanelModel.ts @@ -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() { diff --git a/public/app/features/dashboard/state/PanelQueryRunner.ts b/public/app/features/dashboard/state/PanelQueryRunner.ts index 9bf48f54f32..fe39082fc7a 100644 --- a/public/app/features/dashboard/state/PanelQueryRunner.ts +++ b/public/app/features/dashboard/state/PanelQueryRunner.ts @@ -32,11 +32,11 @@ export interface QueryRunnerOptions< TQuery extends DataQuery = DataQuery, TOptions extends DataSourceJsonData = DataSourceJsonData > { - datasource: string | DataSourceApi; + datasource: string | DataSourceApi | 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; diff --git a/public/app/features/dashboard/state/analyticsProcessor.ts b/public/app/features/dashboard/state/analyticsProcessor.ts index b7a58309db3..dde389fa8c4 100644 --- a/public/app/features/dashboard/state/analyticsProcessor.ts +++ b/public/app/features/dashboard/state/analyticsProcessor.ts @@ -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 diff --git a/public/app/features/dashboard/state/initDashboard.ts b/public/app/features/dashboard/state/initDashboard.ts index b431c498d91..741bf64d551 100644 --- a/public/app/features/dashboard/state/initDashboard.ts +++ b/public/app/features/dashboard/state/initDashboard.ts @@ -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 { } // 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 diff --git a/public/app/features/dashboard/state/runRequest.ts b/public/app/features/dashboard/state/runRequest.ts index 262cbdbcc83..f36e60ab278 100644 --- a/public/app/features/dashboard/state/runRequest.ts +++ b/public/app/features/dashboard/state/runRequest.ts @@ -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; diff --git a/public/app/features/dashboard/utils/getPanelMenu.test.ts b/public/app/features/dashboard/utils/getPanelMenu.test.ts index 2adfd25b287..c8f93b07a91 100644 --- a/public/app/features/dashboard/utils/getPanelMenu.test.ts +++ b/public/app/features/dashboard/utils/getPanelMenu.test.ts @@ -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", + }, + ] + `); }); }); }); diff --git a/public/app/features/dashboard/utils/getPanelMenu.ts b/public/app/features/dashboard/utils/getPanelMenu.ts index 02952f1e93d..1fc36558e7e 100644 --- a/public/app/features/dashboard/utils/getPanelMenu.ts +++ b/public/app/features/dashboard/utils/getPanelMenu.ts @@ -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', diff --git a/public/app/features/dashboard/utils/panel.ts b/public/app/features/dashboard/utils/panel.ts index 2202cdae94a..574ca658620 100644 --- a/public/app/features/dashboard/utils/panel.ts +++ b/public/app/features/dashboard/utils/panel.ts @@ -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, diff --git a/public/app/features/datasources/state/navModel.ts b/public/app/features/datasources/state/navModel.ts index 1790502e2c1..72b8ca1cf57 100644 --- a/public/app/features/datasources/state/navModel.ts +++ b/public/app/features/datasources/state/navModel.ts @@ -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}`, diff --git a/public/app/features/datasources/state/selectors.ts b/public/app/features/datasources/state/selectors.ts index 7837a65ef9e..667ad0d95b4 100644 --- a/public/app/features/datasources/state/selectors.ts +++ b/public/app/features/datasources/state/selectors.ts @@ -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; } diff --git a/public/app/features/datasources/utils/passwordHandlers.test.ts b/public/app/features/datasources/utils/passwordHandlers.test.ts index a83cffb6c8c..7ac628d39bd 100644 --- a/public/app/features/datasources/utils/passwordHandlers.test.ts +++ b/public/app/features/datasources/utils/passwordHandlers.test.ts @@ -18,7 +18,7 @@ describe('createResetHandler', () => { createResetHandler(ctrl, field)(event); expect(ctrl).toEqual({ current: { - [field]: null, + [field]: undefined, secureJsonData: { [field]: '', }, diff --git a/public/app/features/datasources/utils/passwordHandlers.ts b/public/app/features/datasources/utils/passwordHandlers.ts index 25f9298fb3d..9d46f210833 100644 --- a/public/app/features/datasources/utils/passwordHandlers.ts +++ b/public/app/features/datasources/utils/passwordHandlers.ts @@ -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] = ''; diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx index caa846c3c17..52a123497ab 100644 --- a/public/app/features/explore/Explore.tsx +++ b/public/app/features/explore/Explore.tsx @@ -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; diff --git a/public/app/features/explore/ExploreGraphPanel.tsx b/public/app/features/explore/ExploreGraphPanel.tsx index c17066bc095..d6cdb8ce01a 100644 --- a/public/app/features/explore/ExploreGraphPanel.tsx +++ b/public/app/features/explore/ExploreGraphPanel.tsx @@ -40,7 +40,7 @@ const getStyles = (theme: GrafanaTheme) => ({ }); interface Props extends Themeable { - series?: GraphSeriesXY[]; + series?: GraphSeriesXY[] | null; width: number; absoluteRange: AbsoluteTimeRange; loading?: boolean; diff --git a/public/app/features/explore/ExploreToolbar.tsx b/public/app/features/explore/ExploreToolbar.tsx index c3d6bec7053..5f398845969 100644 --- a/public/app/features/explore/ExploreToolbar.tsx +++ b/public/app/features/explore/ExploreToolbar.tsx @@ -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 { if (withChanges) { this.props.setDashboardQueriesToUpdateOnLoad({ - panelId: originPanelId, + panelId: originPanelId!, queries: this.cleanQueries(queries), }); } @@ -235,7 +235,7 @@ export class UnConnectedExploreToolbar extends PureComponent { onChange={this.onChangeDatasource} datasources={getExploreDatasources()} current={this.getSelectedDatasource()} - showLoading={datasourceLoading} + showLoading={datasourceLoading === true} hideTextValue={showSmallDataSourcePicker} />
diff --git a/public/app/features/explore/LiveTailButton.tsx b/public/app/features/explore/LiveTailButton.tsx index 5794c2f1fae..397b7a0e722 100644 --- a/public/app/features/explore/LiveTailButton.tsx +++ b/public/app/features/explore/LiveTailButton.tsx @@ -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'} /> diff --git a/public/app/features/explore/QueryEditor.tsx b/public/app/features/explore/QueryEditor.tsx index c175bbb1a9e..98e02f8f181 100644 --- a/public/app/features/explore/QueryEditor.tsx +++ b/public/app/features/explore/QueryEditor.tsx @@ -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 { return; } - const { datasource, initialQuery, exploreEvents, range } = this.props; - this.initTimeSrv(range); + const { datasource, initialQuery, exploreEvents } = this.props; const loader = getAngularLoader(); const template = ' '; @@ -92,19 +89,6 @@ export default class QueryEditor extends PureComponent { } } - 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
(this.element = element)} style={{ width: '100%' }} />; } diff --git a/public/app/features/explore/RichHistory/RichHistoryQueriesTab.tsx b/public/app/features/explore/RichHistory/RichHistoryQueriesTab.tsx index 64c45d4bba5..0e0669d4f66 100644 --- a/public/app/features/explore/RichHistory/RichHistoryQueriesTab.tsx +++ b/public/app/features/explore/RichHistory/RichHistoryQueriesTab.tsx @@ -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) => { diff --git a/public/app/features/explore/RichHistory/RichHistoryStarredTab.tsx b/public/app/features/explore/RichHistory/RichHistoryStarredTab.tsx index 4d9cb361df8..efac73550c1 100644 --- a/public/app/features/explore/RichHistory/RichHistoryStarredTab.tsx +++ b/public/app/features/explore/RichHistory/RichHistoryStarredTab.tsx @@ -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) => { diff --git a/public/app/features/explore/RunButton.tsx b/public/app/features/explore/RunButton.tsx index 7f33ca81f69..abd43166c1d 100644 --- a/public/app/features/explore/RunButton.tsx +++ b/public/app/features/explore/RunButton.tsx @@ -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} /> ); diff --git a/public/app/features/explore/TableContainer.tsx b/public/app/features/explore/TableContainer.tsx index 2da451fec50..0e6c672fde5 100644 --- a/public/app/features/explore/TableContainer.tsx +++ b/public/app/features/explore/TableContainer.tsx @@ -47,13 +47,13 @@ export class TableContainer extends PureComponent { 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); }; } } diff --git a/public/app/features/explore/TraceView/TraceView.tsx b/public/app/features/explore/TraceView/TraceView.tsx index 2136fd97a5a..d7b026e15c9 100644 --- a/public/app/features/explore/TraceView/TraceView.tsx +++ b/public/app/features/explore/TraceView/TraceView.tsx @@ -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 ( diff --git a/public/app/features/explore/TraceView/useSearch.ts b/public/app/features/explore/TraceView/useSearch.ts index 522f4c52d2c..4f71acb2af9 100644 --- a/public/app/features/explore/TraceView/useSearch.ts +++ b/public/app/features/explore/TraceView/useSearch.ts @@ -8,7 +8,7 @@ import { TraceSpan } from '@grafana/data'; */ export function useSearch(spans?: TraceSpan[]) { const [search, setSearch] = useState(''); - const spanFindMatches: Set | undefined = useMemo(() => { + const spanFindMatches: Set | undefined | null = useMemo(() => { return search && spans ? filterSpans(search, spans) : undefined; }, [search, spans]); diff --git a/public/app/features/explore/state/actionTypes.ts b/public/app/features/explore/state/actionTypes.ts index d816cbf8cf5..2957c4e1cd8 100644 --- a/public/app/features/explore/state/actionTypes.ts +++ b/public/app/features/explore/state/actionTypes.ts @@ -64,7 +64,7 @@ export interface InitializeExplorePayload { range: TimeRange; mode: ExploreMode; ui: ExploreUIState; - originPanelId: number; + originPanelId?: number | null; } export interface LoadDatasourceMissingPayload { diff --git a/public/app/features/explore/state/actions.ts b/public/app/features/explore/state/actions.ts index 8ceae57c4b1..cbb48d776e4 100644 --- a/public/app/features/explore/state/actions.ts +++ b/public/app/features/explore/state/actions.ts @@ -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 { 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 => { return async dispatch => { @@ -455,6 +455,10 @@ export const runQueries = (exploreId: ExploreId): ThunkResult => { 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 => { 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 => { 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(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(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 { } 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 { export interface NavigateToExploreDependencies { getDataSourceSrv: () => DataSourceSrv; getTimeSrv: () => TimeSrv; - getExploreUrl: (args: GetExploreUrlArguments) => Promise; + getExploreUrl: (args: GetExploreUrlArguments) => Promise; openInNewWindow?: (url: string) => void; } @@ -904,7 +916,7 @@ export const navigateToExplore = ( timeSrv: getTimeSrv(), }); - if (openInNewWindow) { + if (openInNewWindow && path) { openInNewWindow(path); return; } diff --git a/public/app/features/explore/state/reducers.ts b/public/app/features/explore/state/reducers.ts index 693c6c9323e..df16a403edc 100644 --- a/public/app/features/explore/state/reducers.ts +++ b/public/app/features/explore/state/reducers.ts @@ -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; diff --git a/public/app/features/plugins/datasource_srv.ts b/public/app/features/plugins/datasource_srv.ts index 51880df99b6..c50f573b48f 100644 --- a/public/app/features/plugins/datasource_srv.ts +++ b/public/app/features/plugins/datasource_srv.ts @@ -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 { + get(name?: string | null, scopedVars?: ScopedVars): Promise { if (!name) { return this.get(config.defaultDatasource); } diff --git a/public/app/plugins/datasource/dashboard/runSharedRequest.ts b/public/app/plugins/datasource/dashboard/runSharedRequest.ts index 5cd8609335e..4d44686e4d1 100644 --- a/public/app/plugins/datasource/dashboard/runSharedRequest.ts +++ b/public/app/plugins/datasource/dashboard/runSharedRequest.ts @@ -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 => { dashboardId: 0, interval: '', panelId: 0, + intervalMs: 1, scopedVars: {}, timezone: '', app: CoreApp.Dashboard, diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts index 99f1dbb1391..6cddee7636c 100644 --- a/public/app/plugins/datasource/graphite/datasource.ts +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -100,11 +100,15 @@ export class GraphiteDatasource extends DataSourceApi = { requestId: '1', dashboardId: 0, interval: '0', + intervalMs: 10, panelId: 0, scopedVars: {}, range: { diff --git a/public/app/plugins/datasource/loki/components/LokiExploreQueryEditor.test.tsx b/public/app/plugins/datasource/loki/components/LokiExploreQueryEditor.test.tsx index a5c878dd3d5..76963051550 100644 --- a/public/app/plugins/datasource/loki/components/LokiExploreQueryEditor.test.tsx +++ b/public/app/plugins/datasource/loki/components/LokiExploreQueryEditor.test.tsx @@ -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'), diff --git a/public/app/plugins/datasource/loki/components/__snapshots__/LokiExploreQueryEditor.test.tsx.snap b/public/app/plugins/datasource/loki/components/__snapshots__/LokiExploreQueryEditor.test.tsx.snap index ea8d27807d8..0e72fbcb0d7 100644 --- a/public/app/plugins/datasource/loki/components/__snapshots__/LokiExploreQueryEditor.test.tsx.snap +++ b/public/app/plugins/datasource/loki/components/__snapshots__/LokiExploreQueryEditor.test.tsx.snap @@ -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", diff --git a/public/app/plugins/datasource/loki/configuration/DerivedFields.test.tsx b/public/app/plugins/datasource/loki/configuration/DerivedFields.test.tsx index 5bc0c9818e0..3c095e83baa 100644 --- a/public/app/plugins/datasource/loki/configuration/DerivedFields.test.tsx +++ b/public/app/plugins/datasource/loki/configuration/DerivedFields.test.tsx @@ -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( {}} />); }); diff --git a/public/app/plugins/datasource/loki/datasource.test.ts b/public/app/plugins/datasource/loki/datasource.test.ts index 442232888bd..66848c60a22 100644 --- a/public/app/plugins/datasource/loki/datasource.test.ts +++ b/public/app/plugins/datasource/loki/datasource.test.ts @@ -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()); diff --git a/public/app/plugins/datasource/loki/datasource.ts b/public/app/plugins/datasource/loki/datasource.ts index 283bfb07ac0..245e921fd6c 100644 --- a/public/app/plugins/datasource/loki/datasource.ts +++ b/public/app/plugins/datasource/loki/datasource.ts @@ -492,7 +492,7 @@ export class LokiDatasource extends DataSourceApi { 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) { diff --git a/public/app/plugins/datasource/prometheus/components/PromExploreQueryEditor.test.tsx b/public/app/plugins/datasource/prometheus/components/PromExploreQueryEditor.test.tsx index 2a6dcc95467..cfa935f7ff3 100644 --- a/public/app/plugins/datasource/prometheus/components/PromExploreQueryEditor.test.tsx +++ b/public/app/plugins/datasource/prometheus/components/PromExploreQueryEditor.test.tsx @@ -18,6 +18,7 @@ const setup = (renderMethod: any, propOverrides?: object) => { request: { requestId: '1', dashboardId: 1, + intervalMs: 1000, interval: '1s', panelId: 1, range: { diff --git a/public/app/plugins/datasource/prometheus/components/__snapshots__/PromExploreQueryEditor.test.tsx.snap b/public/app/plugins/datasource/prometheus/components/__snapshots__/PromExploreQueryEditor.test.tsx.snap index 2670a2a4237..1e446d1505b 100644 --- a/public/app/plugins/datasource/prometheus/components/__snapshots__/PromExploreQueryEditor.test.tsx.snap +++ b/public/app/plugins/datasource/prometheus/components/__snapshots__/PromExploreQueryEditor.test.tsx.snap @@ -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", diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index e7463f6c3e1..13ed404e723 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -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 } } - _request(url: string, data: Record | null, overrides?: Partial) { - const options: BackendSrvRequest = defaults(overrides || {}, { + _request(url: string, data: Record | null, overrides: Partial = {}) { + const options: BackendSrvRequest = defaults(overrides, { url: this.url + url, method: this.httpMethod, headers: {}, @@ -128,7 +128,7 @@ export class PrometheusDatasource extends DataSourceApi .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 } if (this.basicAuth) { - options.headers.Authorization = this.basicAuth; + options.headers!.Authorization = this.basicAuth; } return getBackendSrv().datasourceRequest(options); diff --git a/public/app/plugins/panel/gettingstarted/GettingStarted.tsx b/public/app/plugins/panel/gettingstarted/GettingStarted.tsx index 1dee246cdd6..46f7b2330e4 100644 --- a/public/app/plugins/panel/gettingstarted/GettingStarted.tsx +++ b/public/app/plugins/panel/gettingstarted/GettingStarted.tsx @@ -68,7 +68,9 @@ export class GettingStarted extends PureComponent { const { id } = this.props; const dashboard = getDashboardSrv().getCurrent(); const panel = dashboard.getPanelById(id); - dashboard.removePanel(panel); + + dashboard.removePanel(panel!); + backendSrv .request({ method: 'PUT', diff --git a/public/app/types/acl.ts b/public/app/types/acl.ts index 977791f4159..18b4a3a490d 100644 --- a/public/app/types/acl.ts +++ b/public/app/types/acl.ts @@ -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 { diff --git a/public/app/types/dashboard.ts b/public/app/types/dashboard.ts index f7edcd96079..d83d5de3f03 100644 --- a/public/app/types/dashboard.ts +++ b/public/app/types/dashboard.ts @@ -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 }; } diff --git a/public/app/types/explore.ts b/public/app/types/explore.ts index e0e91a8495a..a67ff2b114e 100644 --- a/public/app/types/explore.ts +++ b/public/app/types/explore.ts @@ -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;