Commit 7e8bd0c1 by Torkel Ödegaard Committed by GitHub

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
parent 4fc984f7
...@@ -15,20 +15,10 @@ export interface DataSourcePluginOptionsEditorProps<JSONData = DataSourceJsonDat ...@@ -15,20 +15,10 @@ export interface DataSourcePluginOptionsEditorProps<JSONData = DataSourceJsonDat
} }
// Utility type to extract the query type TQuery from a class extending DataSourceApi<TQuery, TOptions> // Utility type to extract the query type TQuery from a class extending DataSourceApi<TQuery, TOptions>
export type DataSourceQueryType<DSType extends DataSourceApi<any, any>> = DSType extends DataSourceApi< export type DataSourceQueryType<DSType> = DSType extends DataSourceApi<infer TQuery, any> ? TQuery : never;
infer TQuery,
infer _TOptions
>
? TQuery
: never;
// Utility type to extract the options type TOptions from a class extending DataSourceApi<TQuery, TOptions> // Utility type to extract the options type TOptions from a class extending DataSourceApi<TQuery, TOptions>
export type DataSourceOptionsType<DSType extends DataSourceApi<any, any>> = DSType extends DataSourceApi< export type DataSourceOptionsType<DSType> = DSType extends DataSourceApi<any, infer TOptions> ? TOptions : never;
infer _TQuery,
infer TOptions
>
? TOptions
: never;
export class DataSourcePlugin< export class DataSourcePlugin<
DSType extends DataSourceApi<TQuery, TOptions>, DSType extends DataSourceApi<TQuery, TOptions>,
...@@ -453,7 +443,6 @@ export interface DataQueryTimings { ...@@ -453,7 +443,6 @@ export interface DataQueryTimings {
} }
export interface QueryFix { export interface QueryFix {
type: string;
label: string; label: string;
action?: QueryFixAction; action?: QueryFixAction;
} }
...@@ -472,6 +461,7 @@ export interface QueryHint { ...@@ -472,6 +461,7 @@ export interface QueryHint {
export interface MetricFindValue { export interface MetricFindValue {
text: string; text: string;
expandable?: boolean;
} }
export interface DataSourceJsonData { export interface DataSourceJsonData {
......
...@@ -230,7 +230,7 @@ kbn.interval_to_ms = (str: string) => { ...@@ -230,7 +230,7 @@ kbn.interval_to_ms = (str: string) => {
return info.sec * 1000 * info.count; 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); const info = kbn.describe_interval(str);
return info.sec * info.count; return info.sec * info.count;
}; };
......
...@@ -58,7 +58,7 @@ export interface GetDataOptions { ...@@ -58,7 +58,7 @@ export interface GetDataOptions {
} }
export class PanelQueryRunner { export class PanelQueryRunner {
private subject?: ReplaySubject<PanelData>; private subject: ReplaySubject<PanelData>;
private subscription?: Unsubscribable; private subscription?: Unsubscribable;
private lastResult?: PanelData; private lastResult?: PanelData;
private dataConfigSource: DataConfigSource; private dataConfigSource: DataConfigSource;
...@@ -244,7 +244,7 @@ export class PanelQueryRunner { ...@@ -244,7 +244,7 @@ export class PanelQueryRunner {
} }
} }
getLastResult(): PanelData { getLastResult(): PanelData | undefined {
return this.lastResult; return this.lastResult;
} }
} }
......
...@@ -265,9 +265,9 @@ export class TemplateSrv implements BaseTemplateSrv { ...@@ -265,9 +265,9 @@ export class TemplateSrv implements BaseTemplateSrv {
return variableName; return variableName;
} }
variableExists(expression: string) { variableExists(expression: string): boolean {
const name = this.getVariableName(expression); const name = this.getVariableName(expression);
return name && this.getVariableAtIndex(name) !== void 0; return (name && this.getVariableAtIndex(name)) !== undefined;
} }
highlightVariablesAsHtml(str: string) { highlightVariablesAsHtml(str: string) {
......
...@@ -77,6 +77,7 @@ exports[`LokiExploreQueryEditor should render component 1`] = ` ...@@ -77,6 +77,7 @@ exports[`LokiExploreQueryEditor should render component 1`] = `
}, },
Symbol(length): 0, Symbol(length): 0,
}, },
"logLabelFetchTs": 0,
"request": [Function], "request": [Function],
"seriesCache": LRUCache { "seriesCache": LRUCache {
Symbol(max): 10, Symbol(max): 10,
......
...@@ -8,7 +8,7 @@ import { ArrayVector, Field, FieldType, LinkModel } from '@grafana/data'; ...@@ -8,7 +8,7 @@ import { ArrayVector, Field, FieldType, LinkModel } from '@grafana/data';
import { getFieldLinksForExplore } from '../../../../features/explore/utils/links'; import { getFieldLinksForExplore } from '../../../../features/explore/utils/links';
type Props = { type Props = {
derivedFields: DerivedFieldConfig[]; derivedFields?: DerivedFieldConfig[];
className?: string; className?: string;
}; };
export const DebugSection = (props: Props) => { export const DebugSection = (props: Props) => {
...@@ -92,7 +92,7 @@ function makeDebugFields(derivedFields: DerivedFieldConfig[], debugText: string) ...@@ -92,7 +92,7 @@ function makeDebugFields(derivedFields: DerivedFieldConfig[], debugText: string)
try { try {
const testMatch = debugText.match(field.matcherRegex); const testMatch = debugText.match(field.matcherRegex);
const value = testMatch && testMatch[1]; const value = testMatch && testMatch[1];
let link: LinkModel<Field> = null; let link: LinkModel<Field> | null = null;
if (field.url && value) { if (field.url && value) {
link = getFieldLinksForExplore( link = getFieldLinksForExplore(
...@@ -116,7 +116,6 @@ function makeDebugFields(derivedFields: DerivedFieldConfig[], debugText: string) ...@@ -116,7 +116,6 @@ function makeDebugFields(derivedFields: DerivedFieldConfig[], debugText: string)
href: link && link.href, href: link && link.href,
} as DebugField; } as DebugField;
} catch (error) { } catch (error) {
console.error(error);
return { return {
name: field.name, name: field.name,
error, error,
......
...@@ -20,6 +20,7 @@ type Props = { ...@@ -20,6 +20,7 @@ type Props = {
value?: DerivedFieldConfig[]; value?: DerivedFieldConfig[];
onChange: (value: DerivedFieldConfig[]) => void; onChange: (value: DerivedFieldConfig[]) => void;
}; };
export const DerivedFields = (props: Props) => { export const DerivedFields = (props: Props) => {
const { value, onChange } = props; const { value, onChange } = props;
const theme = useTheme(); const theme = useTheme();
......
...@@ -370,7 +370,7 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> { ...@@ -370,7 +370,7 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> {
getTime(date: string | DateTime, roundUp: boolean) { getTime(date: string | DateTime, roundUp: boolean) {
if (typeof date === 'string') { if (typeof date === 'string') {
date = dateMath.parse(date, roundUp); date = dateMath.parse(date, roundUp)!;
} }
return Math.ceil(date.valueOf() * 1e6); return Math.ceil(date.valueOf() * 1e6);
...@@ -517,9 +517,9 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> { ...@@ -517,9 +517,9 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> {
return annotations; return annotations;
} }
showContextToggle = (row?: LogRowModel) => { showContextToggle(row?: LogRowModel): boolean {
return row && row.searchWords && row.searchWords.length > 0; return (row && row.searchWords && row.searchWords.length > 0) === true;
}; }
throwUnless = (err: any, condition: boolean, target: LokiQuery) => { throwUnless = (err: any, condition: boolean, target: LokiQuery) => {
if (condition) { if (condition) {
......
...@@ -58,7 +58,7 @@ export function addHistoryMetadata(item: CompletionItem, history: LokiHistoryIte ...@@ -58,7 +58,7 @@ export function addHistoryMetadata(item: CompletionItem, history: LokiHistoryIte
export default class LokiLanguageProvider extends LanguageProvider { export default class LokiLanguageProvider extends LanguageProvider {
labelKeys: string[]; labelKeys: string[];
logLabelOptions: any[]; logLabelOptions: any[];
logLabelFetchTs?: number; logLabelFetchTs: number;
started: boolean; started: boolean;
initialRange: AbsoluteTimeRange; initialRange: AbsoluteTimeRange;
datasource: LokiDatasource; datasource: LokiDatasource;
...@@ -77,6 +77,7 @@ export default class LokiLanguageProvider extends LanguageProvider { ...@@ -77,6 +77,7 @@ export default class LokiLanguageProvider extends LanguageProvider {
this.datasource = datasource; this.datasource = datasource;
this.labelKeys = []; this.labelKeys = [];
this.logLabelFetchTs = 0;
Object.assign(this, initialValues); Object.assign(this, initialValues);
} }
...@@ -127,6 +128,11 @@ export default class LokiLanguageProvider extends LanguageProvider { ...@@ -127,6 +128,11 @@ export default class LokiLanguageProvider extends LanguageProvider {
*/ */
async provideCompletionItems(input: TypeaheadInput, context?: TypeaheadContext): Promise<TypeaheadOutput> { async provideCompletionItems(input: TypeaheadInput, context?: TypeaheadContext): Promise<TypeaheadOutput> {
const { wrapperClasses, value, prefix, text } = input; const { wrapperClasses, value, prefix, text } = input;
const emptyResult: TypeaheadOutput = { suggestions: [] };
if (!value) {
return emptyResult;
}
// Local text properties // Local text properties
const empty = value?.document.text.length === 0; const empty = value?.document.text.length === 0;
...@@ -169,18 +175,16 @@ export default class LokiLanguageProvider extends LanguageProvider { ...@@ -169,18 +175,16 @@ export default class LokiLanguageProvider extends LanguageProvider {
return this.getTermCompletionItems(); return this.getTermCompletionItems();
} }
return { return emptyResult;
suggestions: [],
};
} }
getBeginningCompletionItems = (context: TypeaheadContext): TypeaheadOutput => { getBeginningCompletionItems = (context?: TypeaheadContext): TypeaheadOutput => {
return { return {
suggestions: [...this.getEmptyCompletionItems(context).suggestions, ...this.getTermCompletionItems().suggestions], suggestions: [...this.getEmptyCompletionItems(context).suggestions, ...this.getTermCompletionItems().suggestions],
}; };
}; };
getEmptyCompletionItems(context: TypeaheadContext): TypeaheadOutput { getEmptyCompletionItems(context?: TypeaheadContext): TypeaheadOutput {
const history = context?.history; const history = context?.history;
const suggestions = []; const suggestions = [];
...@@ -386,7 +390,7 @@ export default class LokiLanguageProvider extends LanguageProvider { ...@@ -386,7 +390,7 @@ export default class LokiLanguageProvider extends LanguageProvider {
async fetchLogLabels(absoluteRange: AbsoluteTimeRange): Promise<any> { async fetchLogLabels(absoluteRange: AbsoluteTimeRange): Promise<any> {
const url = '/loki/api/v1/label'; const url = '/loki/api/v1/label';
try { try {
this.logLabelFetchTs = Date.now(); this.logLabelFetchTs = Date.now().valueOf();
const rangeParams = absoluteRange ? rangeToParams(absoluteRange) : {}; const rangeParams = absoluteRange ? rangeToParams(absoluteRange) : {};
const res = await this.request(url, rangeParams); const res = await this.request(url, rangeParams);
this.labelKeys = res.slice().sort(); this.labelKeys = res.slice().sort();
...@@ -398,7 +402,7 @@ export default class LokiLanguageProvider extends LanguageProvider { ...@@ -398,7 +402,7 @@ export default class LokiLanguageProvider extends LanguageProvider {
} }
async refreshLogLabels(absoluteRange: AbsoluteTimeRange, forceRefresh?: boolean) { 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); await this.fetchLogLabels(absoluteRange);
} }
} }
...@@ -409,7 +413,7 @@ export default class LokiLanguageProvider extends LanguageProvider { ...@@ -409,7 +413,7 @@ export default class LokiLanguageProvider extends LanguageProvider {
* @param name * @param name
*/ */
fetchSeriesLabels = async (match: string, absoluteRange: AbsoluteTimeRange): Promise<Record<string, string[]>> => { fetchSeriesLabels = async (match: string, absoluteRange: AbsoluteTimeRange): Promise<Record<string, string[]>> => {
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 url = '/loki/api/v1/series';
const { start, end } = rangeParams; const { start, end } = rangeParams;
...@@ -447,7 +451,7 @@ export default class LokiLanguageProvider extends LanguageProvider { ...@@ -447,7 +451,7 @@ export default class LokiLanguageProvider extends LanguageProvider {
async fetchLabelValues(key: string, absoluteRange: AbsoluteTimeRange): Promise<string[]> { async fetchLabelValues(key: string, absoluteRange: AbsoluteTimeRange): Promise<string[]> {
const url = `/loki/api/v1/label/${key}/values`; const url = `/loki/api/v1/label/${key}/values`;
let values: string[] = []; 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 { start, end } = rangeParams;
const cacheKey = this.generateCacheKey(url, start, end, key); const cacheKey = this.generateCacheKey(url, start, end, key);
......
...@@ -18,7 +18,7 @@ describe('getHighlighterExpressionsFromQuery', () => { ...@@ -18,7 +18,7 @@ describe('getHighlighterExpressionsFromQuery', () => {
}); });
it('returns null if filter term is not wrapped in double quotes', () => { 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', () => { it('escapes filter term if regex filter operator is not used', () => {
......
import escapeRegExp from 'lodash/escapeRegExp'; import escapeRegExp from 'lodash/escapeRegExp';
export function formatQuery(selector: string): string { export function formatQuery(selector: string | undefined): string {
return `${selector || ''}`.trim(); return `${selector || ''}`.trim();
} }
...@@ -11,6 +11,7 @@ export function formatQuery(selector: string): string { ...@@ -11,6 +11,7 @@ export function formatQuery(selector: string): string {
export function getHighlighterExpressionsFromQuery(input: string): string[] { export function getHighlighterExpressionsFromQuery(input: string): string[] {
let expression = input; let expression = input;
const results = []; const results = [];
// Consume filter expression from left to right // Consume filter expression from left to right
while (expression) { while (expression) {
const filterStart = expression.search(/\|=|\|~|!=|!~/); const filterStart = expression.search(/\|=|\|~|!=|!~/);
...@@ -43,8 +44,9 @@ export function getHighlighterExpressionsFromQuery(input: string): string[] { ...@@ -43,8 +44,9 @@ export function getHighlighterExpressionsFromQuery(input: string): string[] {
const regexOperator = filterOperator === '|~'; const regexOperator = filterOperator === '|~';
results.push(regexOperator ? unwrappedFilterTerm : escapeRegExp(unwrappedFilterTerm)); results.push(regexOperator ? unwrappedFilterTerm : escapeRegExp(unwrappedFilterTerm));
} else { } else {
return null; return [];
} }
} }
return results; return results;
} }
...@@ -15,6 +15,7 @@ import { ...@@ -15,6 +15,7 @@ import {
Field, Field,
QueryResultMetaStat, QueryResultMetaStat,
QueryResultMeta, QueryResultMeta,
TimeSeriesValue,
} from '@grafana/data'; } from '@grafana/data';
import templateSrv from 'app/features/templating/template_srv'; import templateSrv from 'app/features/templating/template_srv';
...@@ -154,16 +155,14 @@ function lokiMatrixToTimeSeries(matrixResult: LokiMatrixResult, options: Transfo ...@@ -154,16 +155,14 @@ function lokiMatrixToTimeSeries(matrixResult: LokiMatrixResult, options: Transfo
}; };
} }
function lokiPointsToTimeseriesPoints( function lokiPointsToTimeseriesPoints(data: Array<[number, string]>, options: TransformerOptions): TimeSeriesValue[][] {
data: Array<[number, string]>,
options: TransformerOptions
): Array<[number, number]> {
const stepMs = options.step * 1000; const stepMs = options.step * 1000;
const datapoints: Array<[number, number]> = []; const datapoints: TimeSeriesValue[][] = [];
let baseTimestampMs = options.start / 1e6; let baseTimestampMs = options.start / 1e6;
for (const [time, value] of data) { for (const [time, value] of data) {
let datapointValue = parseFloat(value); let datapointValue: TimeSeriesValue = parseFloat(value);
if (isNaN(datapointValue)) { if (isNaN(datapointValue)) {
datapointValue = null; datapointValue = null;
} }
...@@ -198,7 +197,7 @@ export function lokiResultsToTableModel( ...@@ -198,7 +197,7 @@ export function lokiResultsToTableModel(
// Collect all labels across all metrics // Collect all labels across all metrics
const metricLabels: Set<string> = new Set<string>( const metricLabels: Set<string> = new Set<string>(
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 // Sort metric labels, create columns for them and record their index
...@@ -245,9 +244,9 @@ function createMetricLabel(labelData: { [key: string]: string }, options?: Trans ...@@ -245,9 +244,9 @@ function createMetricLabel(labelData: { [key: string]: string }, options?: Trans
let label = let label =
options === undefined || _.isEmpty(options.legendFormat) options === undefined || _.isEmpty(options.legendFormat)
? getOriginalMetricName(labelData) ? getOriginalMetricName(labelData)
: renderTemplate(templateSrv.replace(options.legendFormat), labelData); : renderTemplate(templateSrv.replace(options.legendFormat ?? ''), labelData);
if (!label) { if (!label && options) {
label = options.query; label = options.query;
} }
return label; return label;
...@@ -272,11 +271,13 @@ export function decamelize(s: string): string { ...@@ -272,11 +271,13 @@ export function decamelize(s: string): string {
} }
// Turn loki stats { metric: value } into meta stat { title: metric, value: value } // 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[] = []; const result: QueryResultMetaStat[] = [];
if (!stats) { if (!stats) {
return result; return result;
} }
for (const section in stats) { for (const section in stats) {
const values = stats[section]; const values = stats[section];
for (const label in values) { for (const label in values) {
...@@ -293,6 +294,7 @@ function lokiStatsToMetaStat(stats: LokiStats): QueryResultMetaStat[] { ...@@ -293,6 +294,7 @@ function lokiStatsToMetaStat(stats: LokiStats): QueryResultMetaStat[] {
result.push({ displayName: title, value, unit }); result.push({ displayName: title, value, unit });
} }
} }
return result; return result;
} }
...@@ -309,6 +311,7 @@ export function lokiStreamsToDataframes( ...@@ -309,6 +311,7 @@ export function lokiStreamsToDataframes(
const custom = { const custom = {
lokiQueryStatKey: 'Summary: total bytes processed', lokiQueryStatKey: 'Summary: total bytes processed',
}; };
const series: DataFrame[] = data.map(stream => { const series: DataFrame[] = data.map(stream => {
const dataFrame = lokiStreamResultToDataFrame(stream, reverse); const dataFrame = lokiStreamResultToDataFrame(stream, reverse);
enhanceDataFrame(dataFrame, config); enhanceDataFrame(dataFrame, config);
...@@ -406,10 +409,10 @@ export function rangeQueryResponseToTimeSeries( ...@@ -406,10 +409,10 @@ export function rangeQueryResponseToTimeSeries(
const transformerOptions: TransformerOptions = { const transformerOptions: TransformerOptions = {
format: target.format, format: target.format,
legendFormat: target.legendFormat, legendFormat: target.legendFormat ?? '',
start: query.start, start: query.start!,
end: query.end, end: query.end!,
step: query.step, step: query.step!,
query: query.query, query: query.query,
responseListLength, responseListLength,
refId: target.refId, refId: target.refId,
......
...@@ -59,7 +59,7 @@ export interface LokiVectorResponse { ...@@ -59,7 +59,7 @@ export interface LokiVectorResponse {
} }
export interface LokiMatrixResult { export interface LokiMatrixResult {
metric: { [label: string]: string }; metric: Record<string, string>;
values: Array<[number, string]>; values: Array<[number, string]>;
} }
...@@ -115,8 +115,8 @@ export type DerivedFieldConfig = { ...@@ -115,8 +115,8 @@ export type DerivedFieldConfig = {
}; };
export interface TransformerOptions { export interface TransformerOptions {
format: string; format?: string;
legendFormat: string; legendFormat?: string;
step: number; step: number;
start: number; start: number;
end: number; end: number;
......
...@@ -38,14 +38,17 @@ export class MixedDatasource extends DataSourceApi<DataQuery> { ...@@ -38,14 +38,17 @@ export class MixedDatasource extends DataSourceApi<DataQuery> {
// Build groups of queries to run in parallel // Build groups of queries to run in parallel
const sets: { [key: string]: DataQuery[] } = groupBy(queries, 'datasource'); const sets: { [key: string]: DataQuery[] } = groupBy(queries, 'datasource');
const mixed: BatchedQueries[] = []; const mixed: BatchedQueries[] = [];
for (const key in sets) { for (const key in sets) {
const targets = sets[key]; const targets = sets[key];
const dsName: string | undefined = targets[0].datasource; const dsName = targets[0].datasource;
mixed.push({ mixed.push({
datasource: getDataSourceSrv().get(dsName, request.scopedVars), datasource: getDataSourceSrv().get(dsName, request.scopedVars),
targets, targets,
}); });
} }
return this.batchQueries(mixed, request); return this.batchQueries(mixed, request);
} }
......
...@@ -39,8 +39,8 @@ export default class OpenTsDatasource extends DataSourceApi<OpenTsdbQuery, OpenT ...@@ -39,8 +39,8 @@ export default class OpenTsDatasource extends DataSourceApi<OpenTsdbQuery, OpenT
// Called once per panel (graph) // Called once per panel (graph)
query(options: DataQueryRequest<OpenTsdbQuery>) { query(options: DataQueryRequest<OpenTsdbQuery>) {
const start = this.convertToTSDBTime(options.rangeRaw.from, false, options.timezone); const start = this.convertToTSDBTime(options.range.raw.from, false, options.timezone);
const end = this.convertToTSDBTime(options.rangeRaw.to, true, options.timezone); const end = this.convertToTSDBTime(options.range.raw.to, true, options.timezone);
const qs: any[] = []; const qs: any[] = [];
_.each(options.targets, target => { _.each(options.targets, target => {
......
...@@ -19,7 +19,7 @@ export function PromExploreExtraField(props: PromExploreExtraFieldProps) { ...@@ -19,7 +19,7 @@ export function PromExploreExtraField(props: PromExploreExtraFieldProps) {
return ( return (
<div className="gf-form-inline" aria-label="Prometheus extra field"> <div className="gf-form-inline" aria-label="Prometheus extra field">
<div className="gf-form"> <div className="gf-form">
<InlineFormLabel width={5} tooltip={hasTooltip ? tooltipContent : null}> <InlineFormLabel width={5} tooltip={hasTooltip ? tooltipContent : undefined}>
{label} {label}
</InlineFormLabel> </InlineFormLabel>
<input <input
......
import React, { memo } from 'react'; import React, { memo, FC } from 'react';
// Types // Types
import { ExploreQueryFieldProps } from '@grafana/data'; import { ExploreQueryFieldProps } from '@grafana/data';
...@@ -11,7 +11,7 @@ import { PromExploreExtraField } from './PromExploreExtraField'; ...@@ -11,7 +11,7 @@ import { PromExploreExtraField } from './PromExploreExtraField';
export type Props = ExploreQueryFieldProps<PrometheusDatasource, PromQuery, PromOptions>; export type Props = ExploreQueryFieldProps<PrometheusDatasource, PromQuery, PromOptions>;
export function PromExploreQueryEditor(props: Props) { export const PromExploreQueryEditor: FC<Props> = (props: Props) => {
const { query, data, datasource, history, onChange, onRunQuery } = props; const { query, data, datasource, history, onChange, onRunQuery } = props;
function onChangeQueryStep(value: string) { function onChangeQueryStep(value: string) {
...@@ -55,6 +55,6 @@ export function PromExploreQueryEditor(props: Props) { ...@@ -55,6 +55,6 @@ export function PromExploreQueryEditor(props: Props) {
} }
/> />
); );
} };
export default memo(PromExploreQueryEditor); export default memo(PromExploreQueryEditor);
...@@ -17,7 +17,8 @@ interface State { ...@@ -17,7 +17,8 @@ interface State {
} }
export default class PromLink extends Component<Props, State> { export default class PromLink extends Component<Props, State> {
state: State = { href: null }; state: State = { href: '' };
async componentDidUpdate(prevProps: Props) { async componentDidUpdate(prevProps: Props) {
const { panelData } = this.props; const { panelData } = this.props;
......
...@@ -26,9 +26,9 @@ const INTERVAL_FACTOR_OPTIONS: Array<SelectableValue<number>> = _.map([1, 2, 3, ...@@ -26,9 +26,9 @@ const INTERVAL_FACTOR_OPTIONS: Array<SelectableValue<number>> = _.map([1, 2, 3,
})); }));
interface State { interface State {
legendFormat: string; legendFormat?: string;
formatOption: SelectableValue<string>; formatOption: SelectableValue<string>;
interval: string; interval?: string;
intervalFactorOption: SelectableValue<number>; intervalFactorOption: SelectableValue<number>;
instant: boolean; instant: boolean;
} }
......
...@@ -184,7 +184,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF ...@@ -184,7 +184,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
const result = isDataFrame(data.series[0]) ? data.series.map(toLegacyResponseData) : data.series; const result = isDataFrame(data.series[0]) ? data.series.map(toLegacyResponseData) : data.series;
const hints = datasource.getQueryHints(query, result); const hints = datasource.getQueryHints(query, result);
const hint = hints && hints.length > 0 ? hints[0] : null; const hint = hints.length > 0 ? hints[0] : null;
this.setState({ hint }); this.setState({ hint });
}; };
...@@ -250,7 +250,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF ...@@ -250,7 +250,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
const { datasource, query, onChange, onRunQuery } = this.props; const { datasource, query, onChange, onRunQuery } = this.props;
const { hint } = this.state; const { hint } = this.state;
onChange(datasource.modifyQuery(query, hint.fix.action)); onChange(datasource.modifyQuery(query, hint!.fix!.action));
onRunQuery(); onRunQuery();
}; };
...@@ -277,7 +277,8 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF ...@@ -277,7 +277,8 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
: metricsByPrefix; : metricsByPrefix;
// Hint for big disabled lookups // Hint for big disabled lookups
let hint: QueryHint; let hint: QueryHint | null = null;
if (!datasource.lookupsDisabled && languageProvider.lookupsDisabled) { if (!datasource.lookupsDisabled && languageProvider.lookupsDisabled) {
hint = { hint = {
label: `Dynamic label lookup is disabled for datasources with more than ${lookupMetricsThreshold} metrics.`, label: `Dynamic label lookup is disabled for datasources with more than ${lookupMetricsThreshold} metrics.`,
......
...@@ -9,7 +9,6 @@ exports[`PrometheusExploreExtraField should render component 1`] = ` ...@@ -9,7 +9,6 @@ exports[`PrometheusExploreExtraField should render component 1`] = `
className="gf-form" className="gf-form"
> >
<Component <Component
tooltip={null}
width={5} width={5}
> >
Prometheus Explore Extra Field Prometheus Explore Extra Field
......
...@@ -78,7 +78,7 @@ export const PromSettings = (props: Props) => { ...@@ -78,7 +78,7 @@ export const PromSettings = (props: Props) => {
<div className="gf-form-group"> <div className="gf-form-group">
<div className="gf-form"> <div className="gf-form">
<Switch <Switch
checked={options.jsonData.disableMetricsLookup} checked={options.jsonData.disableMetricsLookup ?? false}
label="Disable metrics lookup" label="Disable metrics lookup"
labelClass="width-14" labelClass="width-14"
onChange={onUpdateDatasourceJsonDataOptionChecked(props, 'disableMetricsLookup')} onChange={onUpdateDatasourceJsonDataOptionChecked(props, 'disableMetricsLookup')}
......
...@@ -26,7 +26,7 @@ import { filter, map, tap } from 'rxjs/operators'; ...@@ -26,7 +26,7 @@ import { filter, map, tap } from 'rxjs/operators';
import PrometheusMetricFindQuery from './metric_find_query'; import PrometheusMetricFindQuery from './metric_find_query';
import { ResultTransformer } from './result_transformer'; import { ResultTransformer } from './result_transformer';
import PrometheusLanguageProvider from './language_provider'; import PrometheusLanguageProvider from './language_provider';
import { getBackendSrv } from '@grafana/runtime'; import { getBackendSrv, BackendSrvRequest } from '@grafana/runtime';
import addLabelToQuery from './add_label_to_query'; import addLabelToQuery from './add_label_to_query';
import { getQueryHints } from './query_hints'; import { getQueryHints } from './query_hints';
import { expandRecordingRules } from './language_utils'; import { expandRecordingRules } from './language_utils';
...@@ -39,17 +39,6 @@ import TableModel from 'app/core/table_model'; ...@@ -39,17 +39,6 @@ import TableModel from 'app/core/table_model';
export const ANNOTATION_QUERY_STEP_DEFAULT = '60s'; export const ANNOTATION_QUERY_STEP_DEFAULT = '60s';
interface RequestOptions {
method?: string;
url?: string;
headers?: Record<string, string>;
transformRequest?: (data: any) => string;
data?: any;
withCredentials?: boolean;
silent?: boolean;
requestId?: string;
}
export interface PromDataQueryResponse { export interface PromDataQueryResponse {
data: { data: {
status: string; status: string;
...@@ -92,7 +81,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -92,7 +81,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
this.type = 'prometheus'; this.type = 'prometheus';
this.editorSrc = 'app/features/prometheus/partials/query.editor.html'; this.editorSrc = 'app/features/prometheus/partials/query.editor.html';
this.url = instanceSettings.url; this.url = instanceSettings.url!;
this.basicAuth = instanceSettings.basicAuth; this.basicAuth = instanceSettings.basicAuth;
this.withCredentials = instanceSettings.withCredentials; this.withCredentials = instanceSettings.withCredentials;
this.interval = instanceSettings.jsonData.timeInterval || '15s'; this.interval = instanceSettings.jsonData.timeInterval || '15s';
...@@ -102,7 +91,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -102,7 +91,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
this.resultTransformer = new ResultTransformer(templateSrv); this.resultTransformer = new ResultTransformer(templateSrv);
this.ruleMappings = {}; this.ruleMappings = {};
this.languageProvider = new PrometheusLanguageProvider(this); this.languageProvider = new PrometheusLanguageProvider(this);
this.lookupsDisabled = instanceSettings.jsonData.disableMetricsLookup; this.lookupsDisabled = instanceSettings.jsonData.disableMetricsLookup ?? false;
this.customQueryParameters = new URLSearchParams(instanceSettings.jsonData.customQueryParameters); this.customQueryParameters = new URLSearchParams(instanceSettings.jsonData.customQueryParameters);
} }
...@@ -123,8 +112,8 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -123,8 +112,8 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
} }
} }
_request(url: string, data: Record<string, string> = {}, options?: RequestOptions) { _request(url: string, data: Record<string, string> | null, overrides?: Partial<BackendSrvRequest>) {
options = defaults(options || {}, { const options: BackendSrvRequest = defaults(overrides || {}, {
url: this.url + url, url: this.url + url,
method: this.httpMethod, method: this.httpMethod,
headers: {}, headers: {},
...@@ -153,7 +142,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -153,7 +142,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
options.headers.Authorization = this.basicAuth; options.headers.Authorization = this.basicAuth;
} }
return getBackendSrv().datasourceRequest(options as Required<RequestOptions>); return getBackendSrv().datasourceRequest(options);
} }
// Use this for tab completion features, wont publish response to other components // Use this for tab completion features, wont publish response to other components
...@@ -271,13 +260,10 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -271,13 +260,10 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
let runningQueriesCount = queries.length; let runningQueriesCount = queries.length;
const subQueries = queries.map((query, index) => { const subQueries = queries.map((query, index) => {
const target = activeTargets[index]; const target = activeTargets[index];
let observable: Observable<any> = null;
if (query.instant) { let observable = query.instant
observable = from(this.performInstantQuery(query, end)); ? from(this.performInstantQuery(query, end))
} else { : from(this.performTimeSeriesQuery(query, query.start, query.end));
observable = from(this.performTimeSeriesQuery(query, query.start, query.end));
}
return observable.pipe( return observable.pipe(
// Decrease the counter here. We assume that each request returns only single value and then completes // 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<PromQuery, PromOptions> ...@@ -301,13 +287,10 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
private panelsQuery(queries: PromQueryRequest[], activeTargets: PromQuery[], end: number, requestId: string) { private panelsQuery(queries: PromQueryRequest[], activeTargets: PromQuery[], end: number, requestId: string) {
const observables: Array<Observable<Array<TableModel | TimeSeries>>> = queries.map((query, index) => { const observables: Array<Observable<Array<TableModel | TimeSeries>>> = queries.map((query, index) => {
const target = activeTargets[index]; const target = activeTargets[index];
let observable: Observable<any> = null;
if (query.instant) { let observable = query.instant
observable = from(this.performInstantQuery(query, end)); ? from(this.performInstantQuery(query, end))
} else { : from(this.performTimeSeriesQuery(query, query.start, query.end));
observable = from(this.performTimeSeriesQuery(query, query.start, query.end));
}
return observable.pipe( return observable.pipe(
filter((response: any) => (response.cancelled ? false : true)), filter((response: any) => (response.cancelled ? false : true)),
...@@ -346,10 +329,10 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -346,10 +329,10 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
const range = Math.ceil(end - start); const range = Math.ceil(end - start);
// options.interval is the dynamically calculated interval // 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 // Minimum interval ("Min step"), if specified for the query or datasource. or same as interval otherwise
const minInterval = kbn.interval_to_seconds( 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; const intervalFactor = target.intervalFactor || 1;
// Adjust the interval to take into account any specified minimum and interval factor plus Prometheus limits // 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<PromQuery, PromOptions> ...@@ -576,7 +559,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
return []; return [];
} }
const step = Math.floor(query.step) * 1000; const step = Math.floor(query.step ?? 15) * 1000;
response?.data?.data?.result?.forEach(series => { response?.data?.data?.result?.forEach(series => {
const tags = Object.entries(series.metric) const tags = Object.entries(series.metric)
...@@ -596,16 +579,17 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -596,16 +579,17 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
}); });
const activeValues = series.values.filter((value: Record<number, string>) => parseFloat(value[1]) >= 1); const activeValues = series.values.filter((value: Record<number, string>) => 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 // Instead of creating singular annotation for each active event we group events into region if they are less
// then `step` apart. // then `step` apart.
let latestEvent: AnnotationEvent = null; let latestEvent: AnnotationEvent | null = null;
activeValuesTimestamps.forEach((timestamp: number) => {
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. // 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; latestEvent.timeEnd = timestamp;
return; continue;
} }
// Event exists but new one is outside of the `step` so we "finish" the current region. // 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<PromQuery, PromOptions> ...@@ -622,7 +606,8 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
tags, tags,
text: self.resultTransformer.renderTemplate(textFormat, series.metric), text: self.resultTransformer.renderTemplate(textFormat, series.metric),
}; };
}); }
if (latestEvent) { if (latestEvent) {
// finish up last point if we have one // finish up last point if we have one
latestEvent.timeEnd = activeValuesTimestamps[activeValuesTimestamps.length - 1]; latestEvent.timeEnd = activeValuesTimestamps[activeValuesTimestamps.length - 1];
...@@ -723,7 +708,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -723,7 +708,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
getPrometheusTime(date: string | DateTime, roundUp: boolean) { getPrometheusTime(date: string | DateTime, roundUp: boolean) {
if (typeof date === 'string') { if (typeof date === 'string') {
date = dateMath.parse(date, roundUp); date = dateMath.parse(date, roundUp)!;
} }
return Math.ceil(date.valueOf() / 1000); return Math.ceil(date.valueOf() / 1000);
......
...@@ -54,9 +54,9 @@ function addMetricsMetadata(metric: string, metadata?: PromMetricsMetadata): Com ...@@ -54,9 +54,9 @@ function addMetricsMetadata(metric: string, metadata?: PromMetricsMetadata): Com
const PREFIX_DELIMITER_REGEX = /(="|!="|=~"|!~"|\{|\[|\(|\+|-|\/|\*|%|\^|\band\b|\bor\b|\bunless\b|==|>=|!=|<=|>|<|=|~|,)/; const PREFIX_DELIMITER_REGEX = /(="|!="|=~"|!~"|\{|\[|\(|\+|-|\/|\*|%|\^|\band\b|\bor\b|\bunless\b|==|>=|!=|<=|>|<|=|~|,)/;
export default class PromQlLanguageProvider extends LanguageProvider { export default class PromQlLanguageProvider extends LanguageProvider {
histogramMetrics?: string[]; histogramMetrics: string[];
timeRange?: { start: number; end: number }; timeRange?: { start: number; end: number };
metrics?: string[]; metrics: string[];
metricsMetadata?: PromMetricsMetadata; metricsMetadata?: PromMetricsMetadata;
startTask: Promise<any>; startTask: Promise<any>;
datasource: PrometheusDatasource; datasource: PrometheusDatasource;
...@@ -87,7 +87,7 @@ export default class PromQlLanguageProvider extends LanguageProvider { ...@@ -87,7 +87,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
// Strip syntax chars so that typeahead suggestions can work on clean inputs // Strip syntax chars so that typeahead suggestions can work on clean inputs
cleanText(s: string) { cleanText(s: string) {
const parts = s.split(PREFIX_DELIMITER_REGEX); const parts = s.split(PREFIX_DELIMITER_REGEX);
const last = parts.pop(); const last = parts.pop()!;
return last return last
.trimLeft() .trimLeft()
.replace(/"$/, '') .replace(/"$/, '')
...@@ -136,6 +136,12 @@ export default class PromQlLanguageProvider extends LanguageProvider { ...@@ -136,6 +136,12 @@ export default class PromQlLanguageProvider extends LanguageProvider {
{ prefix, text, value, labelKey, wrapperClasses }: TypeaheadInput, { prefix, text, value, labelKey, wrapperClasses }: TypeaheadInput,
context: { history: Array<HistoryItem<PromQuery>> } = { history: [] } context: { history: Array<HistoryItem<PromQuery>> } = { history: [] }
): Promise<TypeaheadOutput> => { ): Promise<TypeaheadOutput> => {
const emptyResult: TypeaheadOutput = { suggestions: [] };
if (!value) {
return emptyResult;
}
// Local text properties // Local text properties
const empty = value.document.text.length === 0; const empty = value.document.text.length === 0;
const selectedLines = value.document.getTextsAtRange(value.selection); const selectedLines = value.document.getTextsAtRange(value.selection);
...@@ -179,9 +185,7 @@ export default class PromQlLanguageProvider extends LanguageProvider { ...@@ -179,9 +185,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
return this.getTermCompletionItems(); return this.getTermCompletionItems();
} }
return { return emptyResult;
suggestions: [],
};
}; };
getBeginningCompletionItems = (context: { history: Array<HistoryItem<PromQuery>> }): TypeaheadOutput => { getBeginningCompletionItems = (context: { history: Array<HistoryItem<PromQuery>> }): TypeaheadOutput => {
...@@ -253,7 +257,12 @@ export default class PromQlLanguageProvider extends LanguageProvider { ...@@ -253,7 +257,12 @@ export default class PromQlLanguageProvider extends LanguageProvider {
// Stitch all query lines together to support multi-line queries // Stitch all query lines together to support multi-line queries
let queryOffset; let queryOffset;
const queryText = value.document.getBlocks().reduce((text: string, block) => { 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) { if (value.anchorBlock.key === block.key) {
// Newline characters are not accounted for but this is irrelevant // Newline characters are not accounted for but this is irrelevant
// for the purpose of extracting the selector string // for the purpose of extracting the selector string
...@@ -305,6 +314,10 @@ export default class PromQlLanguageProvider extends LanguageProvider { ...@@ -305,6 +314,10 @@ export default class PromQlLanguageProvider extends LanguageProvider {
labelKey, labelKey,
value, value,
}: TypeaheadInput): Promise<TypeaheadOutput> => { }: TypeaheadInput): Promise<TypeaheadOutput> => {
if (!value) {
return { suggestions: [] };
}
const suggestions: CompletionItemGroup[] = []; const suggestions: CompletionItemGroup[] = [];
const line = value.anchorBlock.getText(); const line = value.anchorBlock.getText();
const cursorOffset = value.selection.anchor.offset; const cursorOffset = value.selection.anchor.offset;
...@@ -346,7 +359,8 @@ export default class PromQlLanguageProvider extends LanguageProvider { ...@@ -346,7 +359,8 @@ export default class PromQlLanguageProvider extends LanguageProvider {
return { suggestions }; return { suggestions };
} }
let context: string; let context: string | undefined;
if ((text && isValueStart) || wrapperClasses.includes('attr-value')) { if ((text && isValueStart) || wrapperClasses.includes('attr-value')) {
// Label values // Label values
if (labelKey && labelValues[labelKey]) { if (labelKey && labelValues[labelKey]) {
......
...@@ -119,19 +119,20 @@ export function expandRecordingRules(query: string, mapping: { [name: string]: s ...@@ -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. // Regex that matches occurences of ){ or }{ or ]{ which is a sign of incorrecly added labels.
const invalidLabelsRegex = /(\)\{|\}\{|\]\{)/; const invalidLabelsRegex = /(\)\{|\}\{|\]\{)/;
const correctlyExpandedQueryArray = queryArray.map(query => { const correctlyExpandedQueryArray = queryArray.map(query => {
let expression = query; return addLabelsToExpression(query, invalidLabelsRegex);
if (expression.match(invalidLabelsRegex)) {
expression = addLabelsToExpression(expression, invalidLabelsRegex);
}
return expression;
}); });
return correctlyExpandedQueryArray.join(''); return correctlyExpandedQueryArray.join('');
} }
function addLabelsToExpression(expr: string, invalidLabelsRegexp: RegExp) { 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. // 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 exprBeforeRegexMatch = expr.substr(0, indexOfRegexMatch + 1);
const exprAfterRegexMatch = expr.substr(indexOfRegexMatch + 1); const exprAfterRegexMatch = expr.substr(indexOfRegexMatch + 1);
......
import _ from 'lodash'; import _ from 'lodash';
import { TimeRange } from '@grafana/data'; import { TimeRange, MetricFindValue } from '@grafana/data';
import { PrometheusDatasource, PromDataQueryResponse } from './datasource'; import { PrometheusDatasource, PromDataQueryResponse } from './datasource';
import { PromQueryRequest } from './types'; import { PromQueryRequest } from './types';
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
...@@ -13,7 +13,7 @@ export default class PrometheusMetricFindQuery { ...@@ -13,7 +13,7 @@ export default class PrometheusMetricFindQuery {
this.range = getTimeSrv().timeRange(); this.range = getTimeSrv().timeRange();
} }
process() { process(): Promise<MetricFindValue[]> {
const labelNamesRegex = /^label_names\(\)\s*$/; const labelNamesRegex = /^label_names\(\)\s*$/;
const labelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]*)\)\s*$/; const labelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]*)\)\s*$/;
const metricNamesRegex = /^metrics\((.+)\)\s*$/; const metricNamesRegex = /^metrics\((.+)\)\s*$/;
...@@ -28,7 +28,7 @@ export default class PrometheusMetricFindQuery { ...@@ -28,7 +28,7 @@ export default class PrometheusMetricFindQuery {
if (labelValuesQuery[1]) { if (labelValuesQuery[1]) {
return this.labelValuesQuery(labelValuesQuery[2], labelValuesQuery[1]); return this.labelValuesQuery(labelValuesQuery[2], labelValuesQuery[1]);
} else { } else {
return this.labelValuesQuery(labelValuesQuery[2], null); return this.labelValuesQuery(labelValuesQuery[2]);
} }
} }
...@@ -136,7 +136,7 @@ export default class PrometheusMetricFindQuery { ...@@ -136,7 +136,7 @@ export default class PrometheusMetricFindQuery {
}); });
} }
metricNameAndLabelsQuery(query: string) { metricNameAndLabelsQuery(query: string): Promise<MetricFindValue[]> {
const start = this.datasource.getPrometheusTime(this.range.from, false); const start = this.datasource.getPrometheusTime(this.range.from, false);
const end = this.datasource.getPrometheusTime(this.range.to, true); const end = this.datasource.getPrometheusTime(this.range.to, true);
const params = new URLSearchParams({ const params = new URLSearchParams({
...@@ -144,10 +144,11 @@ export default class PrometheusMetricFindQuery { ...@@ -144,10 +144,11 @@ export default class PrometheusMetricFindQuery {
start: start.toString(), start: start.toString(),
end: end.toString(), end: end.toString(),
}); });
const url = `/api/v1/series?${params.toString()}`;
const url = `/api/v1/series?${params.toString()}`;
const self = this; 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 _.map(result.data.data, (metric: { [key: string]: string }) => {
return { return {
text: self.datasource.getOriginalMetricName(metric), text: self.datasource.getOriginalMetricName(metric),
......
...@@ -3,11 +3,11 @@ import { PrometheusDatasource } from './datasource'; ...@@ -3,11 +3,11 @@ import { PrometheusDatasource } from './datasource';
describe('getQueryHints()', () => { describe('getQueryHints()', () => {
it('returns no hints for no series', () => { it('returns no hints for no series', () => {
expect(getQueryHints('', [])).toEqual(null); expect(getQueryHints('', [])).toEqual([]);
}); });
it('returns no hints for empty series', () => { 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', () => { it('returns a rate hint for a counter metric', () => {
...@@ -59,7 +59,7 @@ describe('getQueryHints()', () => { ...@@ -59,7 +59,7 @@ describe('getQueryHints()', () => {
// Test substring match not triggering hint // Test substring match not triggering hint
hints = getQueryHints('foo_foo', series, datasource); 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', () => { it('returns no rate hint for a counter metric that already has a rate', () => {
...@@ -72,7 +72,7 @@ describe('getQueryHints()', () => { ...@@ -72,7 +72,7 @@ describe('getQueryHints()', () => {
}, },
]; ];
const hints = getQueryHints('rate(metric_total[1m])', series); 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', () => { it('returns no rate hint for a counter metric that already has an increase', () => {
...@@ -85,7 +85,7 @@ describe('getQueryHints()', () => { ...@@ -85,7 +85,7 @@ describe('getQueryHints()', () => {
}, },
]; ];
const hints = getQueryHints('increase(metric_total[1m])', series); 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', () => { it('returns a rate hint w/o action for a complex counter metric', () => {
......
...@@ -7,7 +7,7 @@ import { PrometheusDatasource } from './datasource'; ...@@ -7,7 +7,7 @@ import { PrometheusDatasource } from './datasource';
*/ */
export const SUM_HINT_THRESHOLD_COUNT = 20; 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 = []; const hints = [];
// ..._bucket metric needs a histogram_quantile() // ..._bucket metric needs a histogram_quantile()
...@@ -32,27 +32,32 @@ export function getQueryHints(query: string, series?: any[], datasource?: Promet ...@@ -32,27 +32,32 @@ export function getQueryHints(query: string, series?: any[], datasource?: Promet
// Use metric metadata for exact types // Use metric metadata for exact types
const nameMatch = query.match(/\b(\w+_(total|sum|count))\b/); const nameMatch = query.match(/\b(\w+_(total|sum|count))\b/);
let counterNameMetric = nameMatch ? nameMatch[1] : ''; let counterNameMetric = nameMatch ? nameMatch[1] : '';
const metricsMetadata = datasource?.languageProvider?.metricsMetadata; const metricsMetadata = datasource?.languageProvider?.metricsMetadata ?? {};
const metricMetadataKeys = Object.keys(metricsMetadata);
let certain = false; let certain = false;
if (_.size(metricsMetadata) > 0) {
counterNameMetric = Object.keys(metricsMetadata).find(metricName => { if (metricMetadataKeys.length > 0) {
// Only considering first type information, could be non-deterministic counterNameMetric =
const metadata = metricsMetadata[metricName][0]; metricMetadataKeys.find(metricName => {
if (metadata.type.toLowerCase() === 'counter') { // Only considering first type information, could be non-deterministic
const metricRegex = new RegExp(`\\b${metricName}\\b`); const metadata = metricsMetadata[metricName][0];
if (query.match(metricRegex)) { if (metadata.type.toLowerCase() === 'counter') {
certain = true; const metricRegex = new RegExp(`\\b${metricName}\\b`);
return true; if (query.match(metricRegex)) {
certain = true;
return true;
}
} }
} return false;
return false; }) ?? '';
});
} }
if (counterNameMetric) { if (counterNameMetric) {
const simpleMetric = query.trim().match(/^\w+$/); const simpleMetric = query.trim().match(/^\w+$/);
const verb = certain ? 'is' : 'looks like'; const verb = certain ? 'is' : 'looks like';
let label = `Metric ${counterNameMetric} ${verb} a counter.`; let label = `Metric ${counterNameMetric} ${verb} a counter.`;
let fix: QueryFix; let fix: QueryFix | undefined;
if (simpleMetric) { if (simpleMetric) {
fix = { fix = {
label: 'Fix by adding rate().', label: 'Fix by adding rate().',
...@@ -60,10 +65,11 @@ export function getQueryHints(query: string, series?: any[], datasource?: Promet ...@@ -60,10 +65,11 @@ export function getQueryHints(query: string, series?: any[], datasource?: Promet
type: 'ADD_RATE', type: 'ADD_RATE',
query, query,
}, },
} as QueryFix; };
} else { } else {
label = `${label} Try applying a rate() function.`; label = `${label} Try applying a rate() function.`;
} }
hints.push({ hints.push({
type: 'APPLY_RATE', type: 'APPLY_RATE',
label, label,
...@@ -119,5 +125,5 @@ export function getQueryHints(query: string, series?: any[], datasource?: Promet ...@@ -119,5 +125,5 @@ export function getQueryHints(query: string, series?: any[], datasource?: Promet
} }
} }
return hints.length > 0 ? hints : null; return hints;
} }
...@@ -20,7 +20,7 @@ export class ResultTransformer { ...@@ -20,7 +20,7 @@ export class ResultTransformer {
), ),
]; ];
} else if (prometheusResult && options.format === 'heatmap') { } else if (prometheusResult && options.format === 'heatmap') {
let seriesList = []; let seriesList: TimeSeries[] = [];
for (const metricData of prometheusResult) { for (const metricData of prometheusResult) {
seriesList.push(this.transformMetricData(metricData, options, options.start, options.end)); seriesList.push(this.transformMetricData(metricData, options, options.start, options.end));
} }
...@@ -28,7 +28,7 @@ export class ResultTransformer { ...@@ -28,7 +28,7 @@ export class ResultTransformer {
seriesList = this.transformToHistogramOverTime(seriesList); seriesList = this.transformToHistogramOverTime(seriesList);
return seriesList; return seriesList;
} else if (prometheusResult) { } else if (prometheusResult) {
const seriesList = []; const seriesList: TimeSeries[] = [];
for (const metricData of prometheusResult) { for (const metricData of prometheusResult) {
if (response.data.data.resultType === 'matrix') { if (response.data.data.resultType === 'matrix') {
seriesList.push(this.transformMetricData(metricData, options, options.start, options.end)); seriesList.push(this.transformMetricData(metricData, options, options.start, options.end));
...@@ -41,7 +41,7 @@ export class ResultTransformer { ...@@ -41,7 +41,7 @@ export class ResultTransformer {
return []; return [];
} }
transformMetricData(metricData: any, options: any, start: number, end: number) { transformMetricData(metricData: any, options: any, start: number, end: number): TimeSeries {
const dps = []; const dps = [];
const { name, labels, title } = this.createLabelInfo(metricData.metric, options); const { name, labels, title } = this.createLabelInfo(metricData.metric, options);
...@@ -53,7 +53,8 @@ export class ResultTransformer { ...@@ -53,7 +53,8 @@ export class ResultTransformer {
} }
for (const value of metricData.values) { for (const value of metricData.values) {
let dpValue = parseFloat(value[1]); let dpValue: number | null = parseFloat(value[1]);
if (_.isNaN(dpValue)) { if (_.isNaN(dpValue)) {
dpValue = null; dpValue = null;
} }
...@@ -73,9 +74,8 @@ export class ResultTransformer { ...@@ -73,9 +74,8 @@ export class ResultTransformer {
return { return {
datapoints: dps, datapoints: dps,
query: options.query,
refId: options.refId, refId: options.refId,
target: name, target: name ?? '',
tags: labels, tags: labels,
title, title,
meta: options.meta, meta: options.meta,
...@@ -147,11 +147,11 @@ export class ResultTransformer { ...@@ -147,11 +147,11 @@ export class ResultTransformer {
return table; return table;
} }
transformInstantMetricData(md: any, options: any) { transformInstantMetricData(md: any, options: any): TimeSeries {
const dps = []; const dps = [];
const { name, labels } = this.createLabelInfo(md.metric, options); const { name, labels } = this.createLabelInfo(md.metric, options);
dps.push([parseFloat(md.value[1]), md.value[0] * 1000]); 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 } { createLabelInfo(labels: { [key: string]: string }, options: any): { name?: string; labels: Labels; title?: string } {
...@@ -210,7 +210,7 @@ export class ResultTransformer { ...@@ -210,7 +210,7 @@ export class ResultTransformer {
for (let j = 0; j < topSeries.length; j++) { for (let j = 0; j < topSeries.length; j++) {
const bottomPoint = bottomSeries[j] || [0]; const bottomPoint = bottomSeries[j] || [0];
topSeries[j][0] -= bottomPoint[0]; topSeries[j][0]! -= bottomPoint[0]!;
} }
} }
......
...@@ -139,7 +139,7 @@ export function useLoadOptions(datasource: ZipkinDatasource) { ...@@ -139,7 +139,7 @@ export function useLoadOptions(datasource: ZipkinDatasource) {
const newTraces = traces.length const newTraces = traces.length
? fromPairs( ? fromPairs(
traces.map(trace => { 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]; return [`${rootSpan.name} [${Math.floor(rootSpan.duration / 1000)} ms]`, rootSpan.traceId];
}) })
...@@ -186,7 +186,8 @@ export function useLoadOptions(datasource: ZipkinDatasource) { ...@@ -186,7 +186,8 @@ export function useLoadOptions(datasource: ZipkinDatasource) {
function useMapToCascaderOptions(services: AsyncState<CascaderOption[]>, allOptions: OptionsState) { function useMapToCascaderOptions(services: AsyncState<CascaderOption[]>, allOptions: OptionsState) {
return useMemo(() => { return useMemo(() => {
let cascaderOptions: CascaderOption[]; let cascaderOptions: CascaderOption[] = [];
if (services.value && services.value.length) { if (services.value && services.value.length) {
cascaderOptions = services.value.map(services => { cascaderOptions = services.value.map(services => {
return { return {
......
...@@ -16,10 +16,9 @@ import { apiPrefix } from './constants'; ...@@ -16,10 +16,9 @@ import { apiPrefix } from './constants';
import { ZipkinSpan } from './types'; import { ZipkinSpan } from './types';
import { transformResponse } from './utils/transforms'; import { transformResponse } from './utils/transforms';
export type ZipkinQuery = { export interface ZipkinQuery extends DataQuery {
// At the moment this should be simply the trace ID to get
query: string; query: string;
} & DataQuery; }
export class ZipkinDatasource extends DataSourceApi<ZipkinQuery> { export class ZipkinDatasource extends DataSourceApi<ZipkinQuery> {
constructor(private instanceSettings: DataSourceInstanceSettings) { constructor(private instanceSettings: DataSourceInstanceSettings) {
......
...@@ -117,8 +117,8 @@ class AlertListPanel extends PanelCtrl { ...@@ -117,8 +117,8 @@ class AlertListPanel extends PanelCtrl {
params.dashboardId = this.dashboard.id; params.dashboardId = this.dashboard.id;
} }
params.from = dateMath.parse(this.dashboard.time.from).unix() * 1000; params.from = dateMath.parse(this.dashboard.time.from)!.unix() * 1000;
params.to = dateMath.parse(this.dashboard.time.to).unix() * 1000; params.to = dateMath.parse(this.dashboard.time.to)!.unix() * 1000;
return promiseToDigest(this.$scope)( return promiseToDigest(this.$scope)(
getBackendSrv() getBackendSrv()
......
...@@ -123,7 +123,7 @@ describe('Graph Panel Migrations', () => { ...@@ -123,7 +123,7 @@ describe('Graph Panel Migrations', () => {
expect(result.dataLinks).toBeUndefined(); expect(result.dataLinks).toBeUndefined();
expect(fieldSource.defaults.links).toHaveLength(1); 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'); expect(link.url).toEqual('THE DRILLDOWN URL');
}); });
}); });
...@@ -429,7 +429,8 @@ class GraphElement { ...@@ -429,7 +429,8 @@ class GraphElement {
// Function for rendering panel // Function for rendering panel
renderPanel() { renderPanel() {
this.panelWidth = this.elem.width(); this.panelWidth = this.elem.width() ?? 0;
if (this.shouldAbortRender()) { if (this.shouldAbortRender()) {
return; return;
} }
......
...@@ -207,14 +207,18 @@ export default function GraphTooltip(this: any, elem: any, dashboard: any, scope ...@@ -207,14 +207,18 @@ export default function GraphTooltip(this: any, elem: any, dashboard: any, scope
self.clear(plot); self.clear(plot);
return; return;
} }
pos.pageX = elem.offset().left + pointOffset.left; pos.pageX = elem.offset().left + pointOffset.left;
pos.pageY = elem.offset().top + elem.height() * pos.panelRelY; 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) { if (!isVisible) {
self.clear(plot); self.clear(plot);
return; return;
} }
plot.setCrosshair(pos); plot.setCrosshair(pos);
allSeriesMode = true; allSeriesMode = true;
......
...@@ -470,8 +470,8 @@ export class EventMarkers { ...@@ -470,8 +470,8 @@ export class EventMarkers {
}, },
left, left,
top, top,
line.width(), line.width() ?? 1,
line.height() line.height() ?? 1
); );
return drawableEvent; return drawableEvent;
...@@ -604,8 +604,8 @@ export class EventMarkers { ...@@ -604,8 +604,8 @@ export class EventMarkers {
}, },
left, left,
top, top,
region.width(), region.width() ?? 1,
region.height() region.height() ?? 1
); );
return drawableEvent; return drawableEvent;
......
...@@ -33,7 +33,7 @@ coreModule.directive('colorLegend', () => { ...@@ -33,7 +33,7 @@ coreModule.directive('colorLegend', () => {
function render() { function render() {
const legendElem = $(elem).find('svg'); const legendElem = $(elem).find('svg');
const legendWidth = Math.floor(legendElem.outerWidth()); const legendWidth = Math.floor(legendElem.outerWidth() ?? 10);
if (panel.color.mode === 'spectrum') { if (panel.color.mode === 'spectrum') {
const colorScheme: any = _.find(ctrl.colorSchemes, { const colorScheme: any = _.find(ctrl.colorSchemes, {
...@@ -102,7 +102,7 @@ function drawColorLegend( ...@@ -102,7 +102,7 @@ function drawColorLegend(
const legend = d3.select(legendElem.get(0)); const legend = d3.select(legendElem.get(0));
clearLegend(elem); clearLegend(elem);
const legendWidth = Math.floor(legendElem.outerWidth()) - 30; const legendWidth = Math.floor(legendElem.outerWidth() ?? 10) - 30;
const legendHeight = legendElem.attr('height'); const legendHeight = legendElem.attr('height');
const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * LEGEND_SEGMENT_WIDTH; const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * LEGEND_SEGMENT_WIDTH;
...@@ -140,7 +140,7 @@ function drawOpacityLegend( ...@@ -140,7 +140,7 @@ function drawOpacityLegend(
const legend = d3.select(legendElem.get(0)); const legend = d3.select(legendElem.get(0));
clearLegend(elem); clearLegend(elem);
const legendWidth = Math.floor(legendElem.outerWidth()) - 30; const legendWidth = Math.floor(legendElem.outerWidth() ?? 30) - 30;
const legendHeight = legendElem.attr('height'); const legendHeight = legendElem.attr('height');
const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * LEGEND_SEGMENT_WIDTH; const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * LEGEND_SEGMENT_WIDTH;
...@@ -214,7 +214,7 @@ function drawSimpleColorLegend(elem: JQuery, colorScale: any) { ...@@ -214,7 +214,7 @@ function drawSimpleColorLegend(elem: JQuery, colorScale: any) {
const legendElem = $(elem).find('svg'); const legendElem = $(elem).find('svg');
clearLegend(elem); clearLegend(elem);
const legendWidth = Math.floor(legendElem.outerWidth()); const legendWidth = Math.floor(legendElem.outerWidth() ?? 30);
const legendHeight = legendElem.attr('height'); const legendHeight = legendElem.attr('height');
if (legendWidth) { if (legendWidth) {
...@@ -242,7 +242,7 @@ function drawSimpleOpacityLegend(elem: JQuery, options: { colorScale: string; ex ...@@ -242,7 +242,7 @@ function drawSimpleOpacityLegend(elem: JQuery, options: { colorScale: string; ex
clearLegend(elem); clearLegend(elem);
const legend = d3.select(legendElem.get(0)); 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'); const legendHeight = legendElem.attr('height');
if (legendWidth) { if (legendWidth) {
......
...@@ -19,7 +19,7 @@ import { ColumnRender, TableRenderModel, ColumnStyle } from './types'; ...@@ -19,7 +19,7 @@ import { ColumnRender, TableRenderModel, ColumnStyle } from './types';
import { ColumnOptionsCtrl } from './column_options'; import { ColumnOptionsCtrl } from './column_options';
export class TableRenderer { export class TableRenderer {
formatters: any[]; formatters: any[] = [];
colorState: any; colorState: any;
constructor( constructor(
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
echo -e "Collecting code stats (typescript errors & more)" echo -e "Collecting code stats (typescript errors & more)"
ERROR_COUNT_LIMIT=700 ERROR_COUNT_LIMIT=600
DIRECTIVES_LIMIT=172 DIRECTIVES_LIMIT=172
CONTROLLERS_LIMIT=139 CONTROLLERS_LIMIT=139
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment