From 7e8bd0c1b77f7b48fdde21cf8a5c9130d57fef00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 6 Jul 2020 21:16:27 +0200 Subject: [PATCH] Chore: Fix typescript strict null errors from 674 -> 565 (#26057) * 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 --- packages/grafana-data/src/types/datasource.ts | 16 +---- public/app/core/utils/kbn.ts | 2 +- .../dashboard/state/PanelQueryRunner.ts | 4 +- .../app/features/templating/template_srv.ts | 4 +- .../LokiExploreQueryEditor.test.tsx.snap | 1 + .../loki/configuration/DebugSection.tsx | 5 +- .../loki/configuration/DerivedFields.tsx | 1 + .../app/plugins/datasource/loki/datasource.ts | 8 +-- .../datasource/loki/language_provider.ts | 24 ++++--- .../datasource/loki/query_utils.test.ts | 2 +- .../plugins/datasource/loki/query_utils.ts | 6 +- .../datasource/loki/result_transformer.ts | 31 ++++----- public/app/plugins/datasource/loki/types.ts | 6 +- .../datasource/mixed/MixedDataSource.ts | 5 +- .../plugins/datasource/opentsdb/datasource.ts | 4 +- .../components/PromExploreExtraField.tsx | 2 +- .../components/PromExploreQueryEditor.tsx | 6 +- .../prometheus/components/PromLink.tsx | 3 +- .../prometheus/components/PromQueryEditor.tsx | 4 +- .../prometheus/components/PromQueryField.tsx | 7 ++- .../PromExploreExtraField.test.tsx.snap | 1 - .../prometheus/configuration/PromSettings.tsx | 2 +- .../datasource/prometheus/datasource.ts | 63 +++++++------------ .../prometheus/language_provider.ts | 30 ++++++--- .../datasource/prometheus/language_utils.ts | 13 ++-- .../prometheus/metric_find_query.ts | 13 ++-- .../datasource/prometheus/query_hints.test.ts | 10 +-- .../datasource/prometheus/query_hints.ts | 40 +++++++----- .../prometheus/result_transformer.ts | 18 +++--- .../plugins/datasource/zipkin/QueryField.tsx | 5 +- .../plugins/datasource/zipkin/datasource.ts | 5 +- public/app/plugins/panel/alertlist/module.ts | 4 +- .../panel/graph/GraphMigrations.test.ts | 2 +- public/app/plugins/panel/graph/graph.ts | 3 +- .../app/plugins/panel/graph/graph_tooltip.ts | 8 ++- .../plugins/panel/graph/jquery.flot.events.ts | 8 +-- .../app/plugins/panel/heatmap/color_legend.ts | 10 +-- .../app/plugins/panel/table-old/renderer.ts | 2 +- scripts/ci-frontend-metrics.sh | 2 +- 39 files changed, 198 insertions(+), 182 deletions(-) diff --git a/packages/grafana-data/src/types/datasource.ts b/packages/grafana-data/src/types/datasource.ts index 9025ada65ec..230cee309fe 100644 --- a/packages/grafana-data/src/types/datasource.ts +++ b/packages/grafana-data/src/types/datasource.ts @@ -15,20 +15,10 @@ export interface DataSourcePluginOptionsEditorProps -export type DataSourceQueryType> = DSType extends DataSourceApi< - infer TQuery, - infer _TOptions -> - ? TQuery - : never; +export type DataSourceQueryType = DSType extends DataSourceApi ? TQuery : never; // Utility type to extract the options type TOptions from a class extending DataSourceApi -export type DataSourceOptionsType> = DSType extends DataSourceApi< - infer _TQuery, - infer TOptions -> - ? TOptions - : never; +export type DataSourceOptionsType = DSType extends DataSourceApi ? TOptions : never; export class DataSourcePlugin< DSType extends DataSourceApi, @@ -453,7 +443,6 @@ export interface DataQueryTimings { } export interface QueryFix { - type: string; label: string; action?: QueryFixAction; } @@ -472,6 +461,7 @@ export interface QueryHint { export interface MetricFindValue { text: string; + expandable?: boolean; } export interface DataSourceJsonData { diff --git a/public/app/core/utils/kbn.ts b/public/app/core/utils/kbn.ts index ca786d56b25..4fc19c35c62 100644 --- a/public/app/core/utils/kbn.ts +++ b/public/app/core/utils/kbn.ts @@ -230,7 +230,7 @@ kbn.interval_to_ms = (str: string) => { return info.sec * 1000 * info.count; }; -kbn.interval_to_seconds = (str: string) => { +kbn.interval_to_seconds = (str: string): number => { const info = kbn.describe_interval(str); return info.sec * info.count; }; diff --git a/public/app/features/dashboard/state/PanelQueryRunner.ts b/public/app/features/dashboard/state/PanelQueryRunner.ts index 1c6ae39e762..9bf48f54f32 100644 --- a/public/app/features/dashboard/state/PanelQueryRunner.ts +++ b/public/app/features/dashboard/state/PanelQueryRunner.ts @@ -58,7 +58,7 @@ export interface GetDataOptions { } export class PanelQueryRunner { - private subject?: ReplaySubject; + private subject: ReplaySubject; private subscription?: Unsubscribable; private lastResult?: PanelData; private dataConfigSource: DataConfigSource; @@ -244,7 +244,7 @@ export class PanelQueryRunner { } } - getLastResult(): PanelData { + getLastResult(): PanelData | undefined { return this.lastResult; } } diff --git a/public/app/features/templating/template_srv.ts b/public/app/features/templating/template_srv.ts index 843c86d198d..75fcfd44d46 100644 --- a/public/app/features/templating/template_srv.ts +++ b/public/app/features/templating/template_srv.ts @@ -265,9 +265,9 @@ export class TemplateSrv implements BaseTemplateSrv { return variableName; } - variableExists(expression: string) { + variableExists(expression: string): boolean { const name = this.getVariableName(expression); - return name && this.getVariableAtIndex(name) !== void 0; + return (name && this.getVariableAtIndex(name)) !== undefined; } highlightVariablesAsHtml(str: string) { 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 3b5b08c7817..ea8d27807d8 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 @@ -77,6 +77,7 @@ exports[`LokiExploreQueryEditor should render component 1`] = ` }, Symbol(length): 0, }, + "logLabelFetchTs": 0, "request": [Function], "seriesCache": LRUCache { Symbol(max): 10, diff --git a/public/app/plugins/datasource/loki/configuration/DebugSection.tsx b/public/app/plugins/datasource/loki/configuration/DebugSection.tsx index 82f3a717c8e..b03cfa06dde 100644 --- a/public/app/plugins/datasource/loki/configuration/DebugSection.tsx +++ b/public/app/plugins/datasource/loki/configuration/DebugSection.tsx @@ -8,7 +8,7 @@ import { ArrayVector, Field, FieldType, LinkModel } from '@grafana/data'; import { getFieldLinksForExplore } from '../../../../features/explore/utils/links'; type Props = { - derivedFields: DerivedFieldConfig[]; + derivedFields?: DerivedFieldConfig[]; className?: string; }; export const DebugSection = (props: Props) => { @@ -92,7 +92,7 @@ function makeDebugFields(derivedFields: DerivedFieldConfig[], debugText: string) try { const testMatch = debugText.match(field.matcherRegex); const value = testMatch && testMatch[1]; - let link: LinkModel = null; + let link: LinkModel | null = null; if (field.url && value) { link = getFieldLinksForExplore( @@ -116,7 +116,6 @@ function makeDebugFields(derivedFields: DerivedFieldConfig[], debugText: string) href: link && link.href, } as DebugField; } catch (error) { - console.error(error); return { name: field.name, error, diff --git a/public/app/plugins/datasource/loki/configuration/DerivedFields.tsx b/public/app/plugins/datasource/loki/configuration/DerivedFields.tsx index 7f9217efcc4..c6208230ed3 100644 --- a/public/app/plugins/datasource/loki/configuration/DerivedFields.tsx +++ b/public/app/plugins/datasource/loki/configuration/DerivedFields.tsx @@ -20,6 +20,7 @@ type Props = { value?: DerivedFieldConfig[]; onChange: (value: DerivedFieldConfig[]) => void; }; + export const DerivedFields = (props: Props) => { const { value, onChange } = props; const theme = useTheme(); diff --git a/public/app/plugins/datasource/loki/datasource.ts b/public/app/plugins/datasource/loki/datasource.ts index b81830e752c..6afe8f939a8 100644 --- a/public/app/plugins/datasource/loki/datasource.ts +++ b/public/app/plugins/datasource/loki/datasource.ts @@ -370,7 +370,7 @@ export class LokiDatasource extends DataSourceApi { getTime(date: string | DateTime, roundUp: boolean) { if (typeof date === 'string') { - date = dateMath.parse(date, roundUp); + date = dateMath.parse(date, roundUp)!; } return Math.ceil(date.valueOf() * 1e6); @@ -517,9 +517,9 @@ export class LokiDatasource extends DataSourceApi { return annotations; } - showContextToggle = (row?: LogRowModel) => { - return row && row.searchWords && row.searchWords.length > 0; - }; + showContextToggle(row?: LogRowModel): boolean { + return (row && row.searchWords && row.searchWords.length > 0) === true; + } throwUnless = (err: any, condition: boolean, target: LokiQuery) => { if (condition) { diff --git a/public/app/plugins/datasource/loki/language_provider.ts b/public/app/plugins/datasource/loki/language_provider.ts index 6a4865c8387..bd1b95bed6e 100644 --- a/public/app/plugins/datasource/loki/language_provider.ts +++ b/public/app/plugins/datasource/loki/language_provider.ts @@ -58,7 +58,7 @@ export function addHistoryMetadata(item: CompletionItem, history: LokiHistoryIte export default class LokiLanguageProvider extends LanguageProvider { labelKeys: string[]; logLabelOptions: any[]; - logLabelFetchTs?: number; + logLabelFetchTs: number; started: boolean; initialRange: AbsoluteTimeRange; datasource: LokiDatasource; @@ -77,6 +77,7 @@ export default class LokiLanguageProvider extends LanguageProvider { this.datasource = datasource; this.labelKeys = []; + this.logLabelFetchTs = 0; Object.assign(this, initialValues); } @@ -127,6 +128,11 @@ export default class LokiLanguageProvider extends LanguageProvider { */ async provideCompletionItems(input: TypeaheadInput, context?: TypeaheadContext): Promise { const { wrapperClasses, value, prefix, text } = input; + const emptyResult: TypeaheadOutput = { suggestions: [] }; + + if (!value) { + return emptyResult; + } // Local text properties const empty = value?.document.text.length === 0; @@ -169,18 +175,16 @@ export default class LokiLanguageProvider extends LanguageProvider { return this.getTermCompletionItems(); } - return { - suggestions: [], - }; + return emptyResult; } - getBeginningCompletionItems = (context: TypeaheadContext): TypeaheadOutput => { + getBeginningCompletionItems = (context?: TypeaheadContext): TypeaheadOutput => { return { suggestions: [...this.getEmptyCompletionItems(context).suggestions, ...this.getTermCompletionItems().suggestions], }; }; - getEmptyCompletionItems(context: TypeaheadContext): TypeaheadOutput { + getEmptyCompletionItems(context?: TypeaheadContext): TypeaheadOutput { const history = context?.history; const suggestions = []; @@ -386,7 +390,7 @@ export default class LokiLanguageProvider extends LanguageProvider { async fetchLogLabels(absoluteRange: AbsoluteTimeRange): Promise { const url = '/loki/api/v1/label'; try { - this.logLabelFetchTs = Date.now(); + this.logLabelFetchTs = Date.now().valueOf(); const rangeParams = absoluteRange ? rangeToParams(absoluteRange) : {}; const res = await this.request(url, rangeParams); this.labelKeys = res.slice().sort(); @@ -398,7 +402,7 @@ export default class LokiLanguageProvider extends LanguageProvider { } async refreshLogLabels(absoluteRange: AbsoluteTimeRange, forceRefresh?: boolean) { - if ((this.labelKeys && Date.now() - this.logLabelFetchTs > LABEL_REFRESH_INTERVAL) || forceRefresh) { + if ((this.labelKeys && Date.now().valueOf() - this.logLabelFetchTs > LABEL_REFRESH_INTERVAL) || forceRefresh) { await this.fetchLogLabels(absoluteRange); } } @@ -409,7 +413,7 @@ export default class LokiLanguageProvider extends LanguageProvider { * @param name */ fetchSeriesLabels = async (match: string, absoluteRange: AbsoluteTimeRange): Promise> => { - const rangeParams: { start?: number; end?: number } = absoluteRange ? rangeToParams(absoluteRange) : {}; + const rangeParams = absoluteRange ? rangeToParams(absoluteRange) : { start: 0, end: 0 }; const url = '/loki/api/v1/series'; const { start, end } = rangeParams; @@ -447,7 +451,7 @@ export default class LokiLanguageProvider extends LanguageProvider { async fetchLabelValues(key: string, absoluteRange: AbsoluteTimeRange): Promise { const url = `/loki/api/v1/label/${key}/values`; let values: string[] = []; - const rangeParams: { start?: number; end?: number } = absoluteRange ? rangeToParams(absoluteRange) : {}; + const rangeParams = absoluteRange ? rangeToParams(absoluteRange) : { start: 0, end: 0 }; const { start, end } = rangeParams; const cacheKey = this.generateCacheKey(url, start, end, key); diff --git a/public/app/plugins/datasource/loki/query_utils.test.ts b/public/app/plugins/datasource/loki/query_utils.test.ts index 0b37cf9e1d7..396bf46a37a 100644 --- a/public/app/plugins/datasource/loki/query_utils.test.ts +++ b/public/app/plugins/datasource/loki/query_utils.test.ts @@ -18,7 +18,7 @@ describe('getHighlighterExpressionsFromQuery', () => { }); it('returns null if filter term is not wrapped in double quotes', () => { - expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= x')).toEqual(null); + expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= x')).toEqual([]); }); it('escapes filter term if regex filter operator is not used', () => { diff --git a/public/app/plugins/datasource/loki/query_utils.ts b/public/app/plugins/datasource/loki/query_utils.ts index 2952f14b3c7..1077f9d1726 100644 --- a/public/app/plugins/datasource/loki/query_utils.ts +++ b/public/app/plugins/datasource/loki/query_utils.ts @@ -1,6 +1,6 @@ import escapeRegExp from 'lodash/escapeRegExp'; -export function formatQuery(selector: string): string { +export function formatQuery(selector: string | undefined): string { return `${selector || ''}`.trim(); } @@ -11,6 +11,7 @@ export function formatQuery(selector: string): string { export function getHighlighterExpressionsFromQuery(input: string): string[] { let expression = input; const results = []; + // Consume filter expression from left to right while (expression) { const filterStart = expression.search(/\|=|\|~|!=|!~/); @@ -43,8 +44,9 @@ export function getHighlighterExpressionsFromQuery(input: string): string[] { const regexOperator = filterOperator === '|~'; results.push(regexOperator ? unwrappedFilterTerm : escapeRegExp(unwrappedFilterTerm)); } else { - return null; + return []; } } + return results; } diff --git a/public/app/plugins/datasource/loki/result_transformer.ts b/public/app/plugins/datasource/loki/result_transformer.ts index c8ea38fd9ec..c448723b42a 100644 --- a/public/app/plugins/datasource/loki/result_transformer.ts +++ b/public/app/plugins/datasource/loki/result_transformer.ts @@ -15,6 +15,7 @@ import { Field, QueryResultMetaStat, QueryResultMeta, + TimeSeriesValue, } from '@grafana/data'; import templateSrv from 'app/features/templating/template_srv'; @@ -154,16 +155,14 @@ function lokiMatrixToTimeSeries(matrixResult: LokiMatrixResult, options: Transfo }; } -function lokiPointsToTimeseriesPoints( - data: Array<[number, string]>, - options: TransformerOptions -): Array<[number, number]> { +function lokiPointsToTimeseriesPoints(data: Array<[number, string]>, options: TransformerOptions): TimeSeriesValue[][] { const stepMs = options.step * 1000; - const datapoints: Array<[number, number]> = []; + const datapoints: TimeSeriesValue[][] = []; let baseTimestampMs = options.start / 1e6; for (const [time, value] of data) { - let datapointValue = parseFloat(value); + let datapointValue: TimeSeriesValue = parseFloat(value); + if (isNaN(datapointValue)) { datapointValue = null; } @@ -198,7 +197,7 @@ export function lokiResultsToTableModel( // Collect all labels across all metrics const metricLabels: Set = new Set( - lokiResults.reduce((acc, cur) => acc.concat(Object.keys(cur.metric)), []) + lokiResults.reduce((acc, cur) => acc.concat(Object.keys(cur.metric)), [] as string[]) ); // Sort metric labels, create columns for them and record their index @@ -245,9 +244,9 @@ function createMetricLabel(labelData: { [key: string]: string }, options?: Trans let label = options === undefined || _.isEmpty(options.legendFormat) ? getOriginalMetricName(labelData) - : renderTemplate(templateSrv.replace(options.legendFormat), labelData); + : renderTemplate(templateSrv.replace(options.legendFormat ?? ''), labelData); - if (!label) { + if (!label && options) { label = options.query; } return label; @@ -272,11 +271,13 @@ export function decamelize(s: string): string { } // Turn loki stats { metric: value } into meta stat { title: metric, value: value } -function lokiStatsToMetaStat(stats: LokiStats): QueryResultMetaStat[] { +function lokiStatsToMetaStat(stats: LokiStats | undefined): QueryResultMetaStat[] { const result: QueryResultMetaStat[] = []; + if (!stats) { return result; } + for (const section in stats) { const values = stats[section]; for (const label in values) { @@ -293,6 +294,7 @@ function lokiStatsToMetaStat(stats: LokiStats): QueryResultMetaStat[] { result.push({ displayName: title, value, unit }); } } + return result; } @@ -309,6 +311,7 @@ export function lokiStreamsToDataframes( const custom = { lokiQueryStatKey: 'Summary: total bytes processed', }; + const series: DataFrame[] = data.map(stream => { const dataFrame = lokiStreamResultToDataFrame(stream, reverse); enhanceDataFrame(dataFrame, config); @@ -406,10 +409,10 @@ export function rangeQueryResponseToTimeSeries( const transformerOptions: TransformerOptions = { format: target.format, - legendFormat: target.legendFormat, - start: query.start, - end: query.end, - step: query.step, + legendFormat: target.legendFormat ?? '', + start: query.start!, + end: query.end!, + step: query.step!, query: query.query, responseListLength, refId: target.refId, diff --git a/public/app/plugins/datasource/loki/types.ts b/public/app/plugins/datasource/loki/types.ts index 794d13f4c3c..ccf611b6ca8 100644 --- a/public/app/plugins/datasource/loki/types.ts +++ b/public/app/plugins/datasource/loki/types.ts @@ -59,7 +59,7 @@ export interface LokiVectorResponse { } export interface LokiMatrixResult { - metric: { [label: string]: string }; + metric: Record; values: Array<[number, string]>; } @@ -115,8 +115,8 @@ export type DerivedFieldConfig = { }; export interface TransformerOptions { - format: string; - legendFormat: string; + format?: string; + legendFormat?: string; step: number; start: number; end: number; diff --git a/public/app/plugins/datasource/mixed/MixedDataSource.ts b/public/app/plugins/datasource/mixed/MixedDataSource.ts index 3fe232cc8b8..9ddc0dc24b1 100644 --- a/public/app/plugins/datasource/mixed/MixedDataSource.ts +++ b/public/app/plugins/datasource/mixed/MixedDataSource.ts @@ -38,14 +38,17 @@ export class MixedDatasource extends DataSourceApi { // Build groups of queries to run in parallel const sets: { [key: string]: DataQuery[] } = groupBy(queries, 'datasource'); const mixed: BatchedQueries[] = []; + for (const key in sets) { const targets = sets[key]; - const dsName: string | undefined = targets[0].datasource; + const dsName = targets[0].datasource; + mixed.push({ datasource: getDataSourceSrv().get(dsName, request.scopedVars), targets, }); } + return this.batchQueries(mixed, request); } diff --git a/public/app/plugins/datasource/opentsdb/datasource.ts b/public/app/plugins/datasource/opentsdb/datasource.ts index e1fff43daad..a76aa425ab0 100644 --- a/public/app/plugins/datasource/opentsdb/datasource.ts +++ b/public/app/plugins/datasource/opentsdb/datasource.ts @@ -39,8 +39,8 @@ export default class OpenTsDatasource extends DataSourceApi) { - const start = this.convertToTSDBTime(options.rangeRaw.from, false, options.timezone); - const end = this.convertToTSDBTime(options.rangeRaw.to, true, options.timezone); + const start = this.convertToTSDBTime(options.range.raw.from, false, options.timezone); + const end = this.convertToTSDBTime(options.range.raw.to, true, options.timezone); const qs: any[] = []; _.each(options.targets, target => { diff --git a/public/app/plugins/datasource/prometheus/components/PromExploreExtraField.tsx b/public/app/plugins/datasource/prometheus/components/PromExploreExtraField.tsx index 3821e83bba7..8c7e300df7a 100644 --- a/public/app/plugins/datasource/prometheus/components/PromExploreExtraField.tsx +++ b/public/app/plugins/datasource/prometheus/components/PromExploreExtraField.tsx @@ -19,7 +19,7 @@ export function PromExploreExtraField(props: PromExploreExtraFieldProps) { return (
- + {label} ; -export function PromExploreQueryEditor(props: Props) { +export const PromExploreQueryEditor: FC = (props: Props) => { const { query, data, datasource, history, onChange, onRunQuery } = props; function onChangeQueryStep(value: string) { @@ -55,6 +55,6 @@ export function PromExploreQueryEditor(props: Props) { } /> ); -} +}; export default memo(PromExploreQueryEditor); diff --git a/public/app/plugins/datasource/prometheus/components/PromLink.tsx b/public/app/plugins/datasource/prometheus/components/PromLink.tsx index 97d6b7a4822..9668281814f 100644 --- a/public/app/plugins/datasource/prometheus/components/PromLink.tsx +++ b/public/app/plugins/datasource/prometheus/components/PromLink.tsx @@ -17,7 +17,8 @@ interface State { } export default class PromLink extends Component { - state: State = { href: null }; + state: State = { href: '' }; + async componentDidUpdate(prevProps: Props) { const { panelData } = this.props; diff --git a/public/app/plugins/datasource/prometheus/components/PromQueryEditor.tsx b/public/app/plugins/datasource/prometheus/components/PromQueryEditor.tsx index 5b5fa0f004b..659cf2a3cd3 100644 --- a/public/app/plugins/datasource/prometheus/components/PromQueryEditor.tsx +++ b/public/app/plugins/datasource/prometheus/components/PromQueryEditor.tsx @@ -26,9 +26,9 @@ const INTERVAL_FACTOR_OPTIONS: Array> = _.map([1, 2, 3, })); interface State { - legendFormat: string; + legendFormat?: string; formatOption: SelectableValue; - interval: string; + interval?: string; intervalFactorOption: SelectableValue; instant: boolean; } diff --git a/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx b/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx index df58d07cfb4..975ef1cd306 100644 --- a/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx +++ b/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx @@ -184,7 +184,7 @@ class PromQueryField extends React.PureComponent 0 ? hints[0] : null; + const hint = hints.length > 0 ? hints[0] : null; this.setState({ hint }); }; @@ -250,7 +250,7 @@ class PromQueryField extends React.PureComponent Prometheus Explore Extra Field diff --git a/public/app/plugins/datasource/prometheus/configuration/PromSettings.tsx b/public/app/plugins/datasource/prometheus/configuration/PromSettings.tsx index 3acbe5f39c2..f026f8c705a 100644 --- a/public/app/plugins/datasource/prometheus/configuration/PromSettings.tsx +++ b/public/app/plugins/datasource/prometheus/configuration/PromSettings.tsx @@ -78,7 +78,7 @@ export const PromSettings = (props: Props) => {
; - transformRequest?: (data: any) => string; - data?: any; - withCredentials?: boolean; - silent?: boolean; - requestId?: string; -} - export interface PromDataQueryResponse { data: { status: string; @@ -92,7 +81,7 @@ export class PrometheusDatasource extends DataSourceApi this.type = 'prometheus'; this.editorSrc = 'app/features/prometheus/partials/query.editor.html'; - this.url = instanceSettings.url; + this.url = instanceSettings.url!; this.basicAuth = instanceSettings.basicAuth; this.withCredentials = instanceSettings.withCredentials; this.interval = instanceSettings.jsonData.timeInterval || '15s'; @@ -102,7 +91,7 @@ export class PrometheusDatasource extends DataSourceApi this.resultTransformer = new ResultTransformer(templateSrv); this.ruleMappings = {}; this.languageProvider = new PrometheusLanguageProvider(this); - this.lookupsDisabled = instanceSettings.jsonData.disableMetricsLookup; + this.lookupsDisabled = instanceSettings.jsonData.disableMetricsLookup ?? false; this.customQueryParameters = new URLSearchParams(instanceSettings.jsonData.customQueryParameters); } @@ -123,8 +112,8 @@ export class PrometheusDatasource extends DataSourceApi } } - _request(url: string, data: Record = {}, options?: RequestOptions) { - options = defaults(options || {}, { + _request(url: string, data: Record | null, overrides?: Partial) { + const options: BackendSrvRequest = defaults(overrides || {}, { url: this.url + url, method: this.httpMethod, headers: {}, @@ -153,7 +142,7 @@ export class PrometheusDatasource extends DataSourceApi options.headers.Authorization = this.basicAuth; } - return getBackendSrv().datasourceRequest(options as Required); + return getBackendSrv().datasourceRequest(options); } // Use this for tab completion features, wont publish response to other components @@ -271,13 +260,10 @@ export class PrometheusDatasource extends DataSourceApi let runningQueriesCount = queries.length; const subQueries = queries.map((query, index) => { const target = activeTargets[index]; - let observable: Observable = null; - if (query.instant) { - observable = from(this.performInstantQuery(query, end)); - } else { - observable = from(this.performTimeSeriesQuery(query, query.start, query.end)); - } + let observable = query.instant + ? from(this.performInstantQuery(query, end)) + : from(this.performTimeSeriesQuery(query, query.start, query.end)); return observable.pipe( // Decrease the counter here. We assume that each request returns only single value and then completes @@ -301,13 +287,10 @@ export class PrometheusDatasource extends DataSourceApi private panelsQuery(queries: PromQueryRequest[], activeTargets: PromQuery[], end: number, requestId: string) { const observables: Array>> = queries.map((query, index) => { const target = activeTargets[index]; - let observable: Observable = null; - if (query.instant) { - observable = from(this.performInstantQuery(query, end)); - } else { - observable = from(this.performTimeSeriesQuery(query, query.start, query.end)); - } + let observable = query.instant + ? from(this.performInstantQuery(query, end)) + : from(this.performTimeSeriesQuery(query, query.start, query.end)); return observable.pipe( filter((response: any) => (response.cancelled ? false : true)), @@ -346,10 +329,10 @@ export class PrometheusDatasource extends DataSourceApi const range = Math.ceil(end - start); // options.interval is the dynamically calculated interval - let interval = kbn.interval_to_seconds(options.interval); + let interval: number = kbn.interval_to_seconds(options.interval); // Minimum interval ("Min step"), if specified for the query or datasource. or same as interval otherwise const minInterval = kbn.interval_to_seconds( - templateSrv.replace(target.interval, options.scopedVars) || options.interval + templateSrv.replace(target.interval || options.interval, options.scopedVars) ); const intervalFactor = target.intervalFactor || 1; // Adjust the interval to take into account any specified minimum and interval factor plus Prometheus limits @@ -576,7 +559,7 @@ export class PrometheusDatasource extends DataSourceApi return []; } - const step = Math.floor(query.step) * 1000; + const step = Math.floor(query.step ?? 15) * 1000; response?.data?.data?.result?.forEach(series => { const tags = Object.entries(series.metric) @@ -596,16 +579,17 @@ export class PrometheusDatasource extends DataSourceApi }); const activeValues = series.values.filter((value: Record) => parseFloat(value[1]) >= 1); - const activeValuesTimestamps = activeValues.map((value: number[]) => value[0]); + const activeValuesTimestamps: number[] = activeValues.map((value: number[]) => value[0]); // Instead of creating singular annotation for each active event we group events into region if they are less // then `step` apart. - let latestEvent: AnnotationEvent = null; - activeValuesTimestamps.forEach((timestamp: number) => { + let latestEvent: AnnotationEvent | null = null; + + for (const timestamp of activeValuesTimestamps) { // We already have event `open` and we have new event that is inside the `step` so we just update the end. - if (latestEvent && latestEvent.timeEnd + step >= timestamp) { + if (latestEvent && (latestEvent.timeEnd ?? 0) + step >= timestamp) { latestEvent.timeEnd = timestamp; - return; + continue; } // Event exists but new one is outside of the `step` so we "finish" the current region. @@ -622,7 +606,8 @@ export class PrometheusDatasource extends DataSourceApi tags, text: self.resultTransformer.renderTemplate(textFormat, series.metric), }; - }); + } + if (latestEvent) { // finish up last point if we have one latestEvent.timeEnd = activeValuesTimestamps[activeValuesTimestamps.length - 1]; @@ -723,7 +708,7 @@ export class PrometheusDatasource extends DataSourceApi getPrometheusTime(date: string | DateTime, roundUp: boolean) { if (typeof date === 'string') { - date = dateMath.parse(date, roundUp); + date = dateMath.parse(date, roundUp)!; } return Math.ceil(date.valueOf() / 1000); diff --git a/public/app/plugins/datasource/prometheus/language_provider.ts b/public/app/plugins/datasource/prometheus/language_provider.ts index 581bd9ac1e6..42188a29a0d 100644 --- a/public/app/plugins/datasource/prometheus/language_provider.ts +++ b/public/app/plugins/datasource/prometheus/language_provider.ts @@ -54,9 +54,9 @@ function addMetricsMetadata(metric: string, metadata?: PromMetricsMetadata): Com const PREFIX_DELIMITER_REGEX = /(="|!="|=~"|!~"|\{|\[|\(|\+|-|\/|\*|%|\^|\band\b|\bor\b|\bunless\b|==|>=|!=|<=|>|<|=|~|,)/; export default class PromQlLanguageProvider extends LanguageProvider { - histogramMetrics?: string[]; + histogramMetrics: string[]; timeRange?: { start: number; end: number }; - metrics?: string[]; + metrics: string[]; metricsMetadata?: PromMetricsMetadata; startTask: Promise; datasource: PrometheusDatasource; @@ -87,7 +87,7 @@ export default class PromQlLanguageProvider extends LanguageProvider { // Strip syntax chars so that typeahead suggestions can work on clean inputs cleanText(s: string) { const parts = s.split(PREFIX_DELIMITER_REGEX); - const last = parts.pop(); + const last = parts.pop()!; return last .trimLeft() .replace(/"$/, '') @@ -136,6 +136,12 @@ export default class PromQlLanguageProvider extends LanguageProvider { { prefix, text, value, labelKey, wrapperClasses }: TypeaheadInput, context: { history: Array> } = { history: [] } ): Promise => { + const emptyResult: TypeaheadOutput = { suggestions: [] }; + + if (!value) { + return emptyResult; + } + // Local text properties const empty = value.document.text.length === 0; const selectedLines = value.document.getTextsAtRange(value.selection); @@ -179,9 +185,7 @@ export default class PromQlLanguageProvider extends LanguageProvider { return this.getTermCompletionItems(); } - return { - suggestions: [], - }; + return emptyResult; }; getBeginningCompletionItems = (context: { history: Array> }): TypeaheadOutput => { @@ -253,7 +257,12 @@ export default class PromQlLanguageProvider extends LanguageProvider { // Stitch all query lines together to support multi-line queries let queryOffset; const queryText = value.document.getBlocks().reduce((text: string, block) => { - const blockText = block.getText(); + if (!block) { + return text; + } + + const blockText = block?.getText(); + if (value.anchorBlock.key === block.key) { // Newline characters are not accounted for but this is irrelevant // for the purpose of extracting the selector string @@ -305,6 +314,10 @@ export default class PromQlLanguageProvider extends LanguageProvider { labelKey, value, }: TypeaheadInput): Promise => { + if (!value) { + return { suggestions: [] }; + } + const suggestions: CompletionItemGroup[] = []; const line = value.anchorBlock.getText(); const cursorOffset = value.selection.anchor.offset; @@ -346,7 +359,8 @@ export default class PromQlLanguageProvider extends LanguageProvider { return { suggestions }; } - let context: string; + let context: string | undefined; + if ((text && isValueStart) || wrapperClasses.includes('attr-value')) { // Label values if (labelKey && labelValues[labelKey]) { diff --git a/public/app/plugins/datasource/prometheus/language_utils.ts b/public/app/plugins/datasource/prometheus/language_utils.ts index 098f5909d4c..34df5f738ab 100644 --- a/public/app/plugins/datasource/prometheus/language_utils.ts +++ b/public/app/plugins/datasource/prometheus/language_utils.ts @@ -119,19 +119,20 @@ export function expandRecordingRules(query: string, mapping: { [name: string]: s // Regex that matches occurences of ){ or }{ or ]{ which is a sign of incorrecly added labels. const invalidLabelsRegex = /(\)\{|\}\{|\]\{)/; const correctlyExpandedQueryArray = queryArray.map(query => { - let expression = query; - if (expression.match(invalidLabelsRegex)) { - expression = addLabelsToExpression(expression, invalidLabelsRegex); - } - return expression; + return addLabelsToExpression(query, invalidLabelsRegex); }); return correctlyExpandedQueryArray.join(''); } function addLabelsToExpression(expr: string, invalidLabelsRegexp: RegExp) { + const match = expr.match(invalidLabelsRegexp); + if (!match) { + return expr; + } + // Split query into 2 parts - before the invalidLabelsRegex match and after. - const indexOfRegexMatch = expr.match(invalidLabelsRegexp).index; + const indexOfRegexMatch = match.index ?? 0; const exprBeforeRegexMatch = expr.substr(0, indexOfRegexMatch + 1); const exprAfterRegexMatch = expr.substr(indexOfRegexMatch + 1); diff --git a/public/app/plugins/datasource/prometheus/metric_find_query.ts b/public/app/plugins/datasource/prometheus/metric_find_query.ts index 7573ec3b4b0..b545bb9822f 100644 --- a/public/app/plugins/datasource/prometheus/metric_find_query.ts +++ b/public/app/plugins/datasource/prometheus/metric_find_query.ts @@ -1,5 +1,5 @@ import _ from 'lodash'; -import { TimeRange } from '@grafana/data'; +import { TimeRange, MetricFindValue } from '@grafana/data'; import { PrometheusDatasource, PromDataQueryResponse } from './datasource'; import { PromQueryRequest } from './types'; import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; @@ -13,7 +13,7 @@ export default class PrometheusMetricFindQuery { this.range = getTimeSrv().timeRange(); } - process() { + process(): Promise { const labelNamesRegex = /^label_names\(\)\s*$/; const labelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]*)\)\s*$/; const metricNamesRegex = /^metrics\((.+)\)\s*$/; @@ -28,7 +28,7 @@ export default class PrometheusMetricFindQuery { if (labelValuesQuery[1]) { return this.labelValuesQuery(labelValuesQuery[2], labelValuesQuery[1]); } else { - return this.labelValuesQuery(labelValuesQuery[2], null); + return this.labelValuesQuery(labelValuesQuery[2]); } } @@ -136,7 +136,7 @@ export default class PrometheusMetricFindQuery { }); } - metricNameAndLabelsQuery(query: string) { + metricNameAndLabelsQuery(query: string): Promise { const start = this.datasource.getPrometheusTime(this.range.from, false); const end = this.datasource.getPrometheusTime(this.range.to, true); const params = new URLSearchParams({ @@ -144,10 +144,11 @@ export default class PrometheusMetricFindQuery { start: start.toString(), end: end.toString(), }); - const url = `/api/v1/series?${params.toString()}`; + const url = `/api/v1/series?${params.toString()}`; const self = this; - return this.datasource.metadataRequest(url).then((result: PromDataQueryResponse) => { + + return this.datasource.metadataRequest(url).then((result: any) => { return _.map(result.data.data, (metric: { [key: string]: string }) => { return { text: self.datasource.getOriginalMetricName(metric), diff --git a/public/app/plugins/datasource/prometheus/query_hints.test.ts b/public/app/plugins/datasource/prometheus/query_hints.test.ts index 6f5f9ce460e..f832af53955 100644 --- a/public/app/plugins/datasource/prometheus/query_hints.test.ts +++ b/public/app/plugins/datasource/prometheus/query_hints.test.ts @@ -3,11 +3,11 @@ import { PrometheusDatasource } from './datasource'; describe('getQueryHints()', () => { it('returns no hints for no series', () => { - expect(getQueryHints('', [])).toEqual(null); + expect(getQueryHints('', [])).toEqual([]); }); it('returns no hints for empty series', () => { - expect(getQueryHints('', [{ datapoints: [] }])).toEqual(null); + expect(getQueryHints('', [{ datapoints: [] }])).toEqual([]); }); it('returns a rate hint for a counter metric', () => { @@ -59,7 +59,7 @@ describe('getQueryHints()', () => { // Test substring match not triggering hint hints = getQueryHints('foo_foo', series, datasource); - expect(hints).toBe(null); + expect(hints).toEqual([]); }); it('returns no rate hint for a counter metric that already has a rate', () => { @@ -72,7 +72,7 @@ describe('getQueryHints()', () => { }, ]; const hints = getQueryHints('rate(metric_total[1m])', series); - expect(hints).toEqual(null); + expect(hints).toEqual([]); }); it('returns no rate hint for a counter metric that already has an increase', () => { @@ -85,7 +85,7 @@ describe('getQueryHints()', () => { }, ]; const hints = getQueryHints('increase(metric_total[1m])', series); - expect(hints).toEqual(null); + expect(hints).toEqual([]); }); it('returns a rate hint w/o action for a complex counter metric', () => { diff --git a/public/app/plugins/datasource/prometheus/query_hints.ts b/public/app/plugins/datasource/prometheus/query_hints.ts index 69de51443dc..e261b650ebd 100644 --- a/public/app/plugins/datasource/prometheus/query_hints.ts +++ b/public/app/plugins/datasource/prometheus/query_hints.ts @@ -7,7 +7,7 @@ import { PrometheusDatasource } from './datasource'; */ export const SUM_HINT_THRESHOLD_COUNT = 20; -export function getQueryHints(query: string, series?: any[], datasource?: PrometheusDatasource): QueryHint[] | null { +export function getQueryHints(query: string, series?: any[], datasource?: PrometheusDatasource): QueryHint[] { const hints = []; // ..._bucket metric needs a histogram_quantile() @@ -32,27 +32,32 @@ export function getQueryHints(query: string, series?: any[], datasource?: Promet // Use metric metadata for exact types const nameMatch = query.match(/\b(\w+_(total|sum|count))\b/); let counterNameMetric = nameMatch ? nameMatch[1] : ''; - const metricsMetadata = datasource?.languageProvider?.metricsMetadata; + const metricsMetadata = datasource?.languageProvider?.metricsMetadata ?? {}; + const metricMetadataKeys = Object.keys(metricsMetadata); let certain = false; - if (_.size(metricsMetadata) > 0) { - counterNameMetric = Object.keys(metricsMetadata).find(metricName => { - // Only considering first type information, could be non-deterministic - const metadata = metricsMetadata[metricName][0]; - if (metadata.type.toLowerCase() === 'counter') { - const metricRegex = new RegExp(`\\b${metricName}\\b`); - if (query.match(metricRegex)) { - certain = true; - return true; + + if (metricMetadataKeys.length > 0) { + counterNameMetric = + metricMetadataKeys.find(metricName => { + // Only considering first type information, could be non-deterministic + const metadata = metricsMetadata[metricName][0]; + if (metadata.type.toLowerCase() === 'counter') { + const metricRegex = new RegExp(`\\b${metricName}\\b`); + if (query.match(metricRegex)) { + certain = true; + return true; + } } - } - return false; - }); + return false; + }) ?? ''; } + if (counterNameMetric) { const simpleMetric = query.trim().match(/^\w+$/); const verb = certain ? 'is' : 'looks like'; let label = `Metric ${counterNameMetric} ${verb} a counter.`; - let fix: QueryFix; + let fix: QueryFix | undefined; + if (simpleMetric) { fix = { label: 'Fix by adding rate().', @@ -60,10 +65,11 @@ export function getQueryHints(query: string, series?: any[], datasource?: Promet type: 'ADD_RATE', query, }, - } as QueryFix; + }; } else { label = `${label} Try applying a rate() function.`; } + hints.push({ type: 'APPLY_RATE', label, @@ -119,5 +125,5 @@ export function getQueryHints(query: string, series?: any[], datasource?: Promet } } - return hints.length > 0 ? hints : null; + return hints; } diff --git a/public/app/plugins/datasource/prometheus/result_transformer.ts b/public/app/plugins/datasource/prometheus/result_transformer.ts index 5f601a2faa6..116bc3b26ff 100644 --- a/public/app/plugins/datasource/prometheus/result_transformer.ts +++ b/public/app/plugins/datasource/prometheus/result_transformer.ts @@ -20,7 +20,7 @@ export class ResultTransformer { ), ]; } else if (prometheusResult && options.format === 'heatmap') { - let seriesList = []; + let seriesList: TimeSeries[] = []; for (const metricData of prometheusResult) { seriesList.push(this.transformMetricData(metricData, options, options.start, options.end)); } @@ -28,7 +28,7 @@ export class ResultTransformer { seriesList = this.transformToHistogramOverTime(seriesList); return seriesList; } else if (prometheusResult) { - const seriesList = []; + const seriesList: TimeSeries[] = []; for (const metricData of prometheusResult) { if (response.data.data.resultType === 'matrix') { seriesList.push(this.transformMetricData(metricData, options, options.start, options.end)); @@ -41,7 +41,7 @@ export class ResultTransformer { return []; } - transformMetricData(metricData: any, options: any, start: number, end: number) { + transformMetricData(metricData: any, options: any, start: number, end: number): TimeSeries { const dps = []; const { name, labels, title } = this.createLabelInfo(metricData.metric, options); @@ -53,7 +53,8 @@ export class ResultTransformer { } for (const value of metricData.values) { - let dpValue = parseFloat(value[1]); + let dpValue: number | null = parseFloat(value[1]); + if (_.isNaN(dpValue)) { dpValue = null; } @@ -73,9 +74,8 @@ export class ResultTransformer { return { datapoints: dps, - query: options.query, refId: options.refId, - target: name, + target: name ?? '', tags: labels, title, meta: options.meta, @@ -147,11 +147,11 @@ export class ResultTransformer { return table; } - transformInstantMetricData(md: any, options: any) { + transformInstantMetricData(md: any, options: any): TimeSeries { const dps = []; const { name, labels } = this.createLabelInfo(md.metric, options); dps.push([parseFloat(md.value[1]), md.value[0] * 1000]); - return { target: name, title: name, datapoints: dps, tags: labels, refId: options.refId, meta: options.meta }; + return { target: name ?? '', title: name, datapoints: dps, tags: labels, refId: options.refId, meta: options.meta }; } createLabelInfo(labels: { [key: string]: string }, options: any): { name?: string; labels: Labels; title?: string } { @@ -210,7 +210,7 @@ export class ResultTransformer { for (let j = 0; j < topSeries.length; j++) { const bottomPoint = bottomSeries[j] || [0]; - topSeries[j][0] -= bottomPoint[0]; + topSeries[j][0]! -= bottomPoint[0]!; } } diff --git a/public/app/plugins/datasource/zipkin/QueryField.tsx b/public/app/plugins/datasource/zipkin/QueryField.tsx index 4e31ed16165..2f3201d09b2 100644 --- a/public/app/plugins/datasource/zipkin/QueryField.tsx +++ b/public/app/plugins/datasource/zipkin/QueryField.tsx @@ -139,7 +139,7 @@ export function useLoadOptions(datasource: ZipkinDatasource) { const newTraces = traces.length ? fromPairs( traces.map(trace => { - const rootSpan = trace.find(span => !span.parentId); + const rootSpan = trace.find(span => !span.parentId)!; return [`${rootSpan.name} [${Math.floor(rootSpan.duration / 1000)} ms]`, rootSpan.traceId]; }) @@ -186,7 +186,8 @@ export function useLoadOptions(datasource: ZipkinDatasource) { function useMapToCascaderOptions(services: AsyncState, allOptions: OptionsState) { return useMemo(() => { - let cascaderOptions: CascaderOption[]; + let cascaderOptions: CascaderOption[] = []; + if (services.value && services.value.length) { cascaderOptions = services.value.map(services => { return { diff --git a/public/app/plugins/datasource/zipkin/datasource.ts b/public/app/plugins/datasource/zipkin/datasource.ts index 4097322e9dc..44f0baf657f 100644 --- a/public/app/plugins/datasource/zipkin/datasource.ts +++ b/public/app/plugins/datasource/zipkin/datasource.ts @@ -16,10 +16,9 @@ import { apiPrefix } from './constants'; import { ZipkinSpan } from './types'; import { transformResponse } from './utils/transforms'; -export type ZipkinQuery = { - // At the moment this should be simply the trace ID to get +export interface ZipkinQuery extends DataQuery { query: string; -} & DataQuery; +} export class ZipkinDatasource extends DataSourceApi { constructor(private instanceSettings: DataSourceInstanceSettings) { diff --git a/public/app/plugins/panel/alertlist/module.ts b/public/app/plugins/panel/alertlist/module.ts index f165508f71a..cfb921ddaaf 100644 --- a/public/app/plugins/panel/alertlist/module.ts +++ b/public/app/plugins/panel/alertlist/module.ts @@ -117,8 +117,8 @@ class AlertListPanel extends PanelCtrl { params.dashboardId = this.dashboard.id; } - params.from = dateMath.parse(this.dashboard.time.from).unix() * 1000; - params.to = dateMath.parse(this.dashboard.time.to).unix() * 1000; + params.from = dateMath.parse(this.dashboard.time.from)!.unix() * 1000; + params.to = dateMath.parse(this.dashboard.time.to)!.unix() * 1000; return promiseToDigest(this.$scope)( getBackendSrv() diff --git a/public/app/plugins/panel/graph/GraphMigrations.test.ts b/public/app/plugins/panel/graph/GraphMigrations.test.ts index 667142f5e12..f0e8fca99bf 100644 --- a/public/app/plugins/panel/graph/GraphMigrations.test.ts +++ b/public/app/plugins/panel/graph/GraphMigrations.test.ts @@ -123,7 +123,7 @@ describe('Graph Panel Migrations', () => { expect(result.dataLinks).toBeUndefined(); expect(fieldSource.defaults.links).toHaveLength(1); - const link = fieldSource.defaults.links[0]; + const link = fieldSource.defaults.links![0]; expect(link.url).toEqual('THE DRILLDOWN URL'); }); }); diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 5f747a5f40f..d6ea5c23187 100644 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -429,7 +429,8 @@ class GraphElement { // Function for rendering panel renderPanel() { - this.panelWidth = this.elem.width(); + this.panelWidth = this.elem.width() ?? 0; + if (this.shouldAbortRender()) { return; } diff --git a/public/app/plugins/panel/graph/graph_tooltip.ts b/public/app/plugins/panel/graph/graph_tooltip.ts index 5a8931b3b9e..9770e04014b 100644 --- a/public/app/plugins/panel/graph/graph_tooltip.ts +++ b/public/app/plugins/panel/graph/graph_tooltip.ts @@ -207,14 +207,18 @@ export default function GraphTooltip(this: any, elem: any, dashboard: any, scope self.clear(plot); return; } + pos.pageX = elem.offset().left + pointOffset.left; pos.pageY = elem.offset().top + elem.height() * pos.panelRelY; - const isVisible = - pos.pageY >= $(window).scrollTop() && pos.pageY <= $(window).innerHeight() + $(window).scrollTop(); + + const scrollTop = $(window).scrollTop() ?? 0; + const isVisible = pos.pageY >= scrollTop && pos.pageY <= $(window).innerHeight()! + scrollTop; + if (!isVisible) { self.clear(plot); return; } + plot.setCrosshair(pos); allSeriesMode = true; diff --git a/public/app/plugins/panel/graph/jquery.flot.events.ts b/public/app/plugins/panel/graph/jquery.flot.events.ts index 22d65f4b242..1dd82f19b60 100644 --- a/public/app/plugins/panel/graph/jquery.flot.events.ts +++ b/public/app/plugins/panel/graph/jquery.flot.events.ts @@ -470,8 +470,8 @@ export class EventMarkers { }, left, top, - line.width(), - line.height() + line.width() ?? 1, + line.height() ?? 1 ); return drawableEvent; @@ -604,8 +604,8 @@ export class EventMarkers { }, left, top, - region.width(), - region.height() + region.width() ?? 1, + region.height() ?? 1 ); return drawableEvent; diff --git a/public/app/plugins/panel/heatmap/color_legend.ts b/public/app/plugins/panel/heatmap/color_legend.ts index a4fe0e03560..ff46d051364 100644 --- a/public/app/plugins/panel/heatmap/color_legend.ts +++ b/public/app/plugins/panel/heatmap/color_legend.ts @@ -33,7 +33,7 @@ coreModule.directive('colorLegend', () => { function render() { const legendElem = $(elem).find('svg'); - const legendWidth = Math.floor(legendElem.outerWidth()); + const legendWidth = Math.floor(legendElem.outerWidth() ?? 10); if (panel.color.mode === 'spectrum') { const colorScheme: any = _.find(ctrl.colorSchemes, { @@ -102,7 +102,7 @@ function drawColorLegend( const legend = d3.select(legendElem.get(0)); clearLegend(elem); - const legendWidth = Math.floor(legendElem.outerWidth()) - 30; + const legendWidth = Math.floor(legendElem.outerWidth() ?? 10) - 30; const legendHeight = legendElem.attr('height'); const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * LEGEND_SEGMENT_WIDTH; @@ -140,7 +140,7 @@ function drawOpacityLegend( const legend = d3.select(legendElem.get(0)); clearLegend(elem); - const legendWidth = Math.floor(legendElem.outerWidth()) - 30; + const legendWidth = Math.floor(legendElem.outerWidth() ?? 30) - 30; const legendHeight = legendElem.attr('height'); const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * LEGEND_SEGMENT_WIDTH; @@ -214,7 +214,7 @@ function drawSimpleColorLegend(elem: JQuery, colorScale: any) { const legendElem = $(elem).find('svg'); clearLegend(elem); - const legendWidth = Math.floor(legendElem.outerWidth()); + const legendWidth = Math.floor(legendElem.outerWidth() ?? 30); const legendHeight = legendElem.attr('height'); if (legendWidth) { @@ -242,7 +242,7 @@ function drawSimpleOpacityLegend(elem: JQuery, options: { colorScale: string; ex clearLegend(elem); const legend = d3.select(legendElem.get(0)); - const legendWidth = Math.floor(legendElem.outerWidth()); + const legendWidth = Math.floor(legendElem.outerWidth() ?? 30); const legendHeight = legendElem.attr('height'); if (legendWidth) { diff --git a/public/app/plugins/panel/table-old/renderer.ts b/public/app/plugins/panel/table-old/renderer.ts index dbd04b865ca..5a63c961775 100644 --- a/public/app/plugins/panel/table-old/renderer.ts +++ b/public/app/plugins/panel/table-old/renderer.ts @@ -19,7 +19,7 @@ import { ColumnRender, TableRenderModel, ColumnStyle } from './types'; import { ColumnOptionsCtrl } from './column_options'; export class TableRenderer { - formatters: any[]; + formatters: any[] = []; colorState: any; constructor( diff --git a/scripts/ci-frontend-metrics.sh b/scripts/ci-frontend-metrics.sh index f8e0cae1f7e..571587aef31 100755 --- a/scripts/ci-frontend-metrics.sh +++ b/scripts/ci-frontend-metrics.sh @@ -2,7 +2,7 @@ echo -e "Collecting code stats (typescript errors & more)" -ERROR_COUNT_LIMIT=700 +ERROR_COUNT_LIMIT=600 DIRECTIVES_LIMIT=172 CONTROLLERS_LIMIT=139