mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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
This commit is contained in:
parent
4fc984f771
commit
7e8bd0c1b7
@ -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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface QueryFix {
|
export interface QueryFix {
|
||||||
type: string;
|
|
||||||
label: string;
|
label: string;
|
||||||
action?: QueryFixAction;
|
action?: QueryFixAction;
|
||||||
}
|
}
|
||||||
@ -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) => {
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getLastResult(): PanelData {
|
getLastResult(): PanelData | undefined {
|
||||||
return this.lastResult;
|
return this.lastResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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`] = `
|
|||||||
},
|
},
|
||||||
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';
|
|||||||
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)
|
|||||||
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)
|
|||||||
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 = {
|
|||||||
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> {
|
|||||||
|
|
||||||
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> {
|
|||||||
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
|
|||||||
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 {
|
|||||||
|
|
||||||
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 {
|
|||||||
*/
|
*/
|
||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|||||||
* @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 {
|
|||||||
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', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
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', () => {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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 {
|
|||||||
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[] {
|
|||||||
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 {
|
|||||||
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
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
|||||||
|
|
||||||
// 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
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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[] {
|
|||||||
result.push({ displayName: title, value, unit });
|
result.push({ displayName: title, value, unit });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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(
|
|||||||
|
|
||||||
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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
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> {
|
|||||||
// 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
|
|||||||
|
|
||||||
// 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) {
|
|||||||
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
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
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';
|
|||||||
|
|
||||||
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) {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default memo(PromExploreQueryEditor);
|
export default memo(PromExploreQueryEditor);
|
||||||
|
@ -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,
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
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
|
|||||||
|
|
||||||
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
|
|||||||
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
|
|||||||
: 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`] = `
|
|||||||
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) => {
|
|||||||
<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';
|
|||||||
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';
|
|||||||
|
|
||||||
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>
|
|||||||
|
|
||||||
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>
|
|||||||
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>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_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>
|
|||||||
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>
|
|||||||
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>
|
|||||||
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>
|
|||||||
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>
|
|||||||
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>
|
|||||||
});
|
});
|
||||||
|
|
||||||
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>
|
|||||||
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>
|
|||||||
|
|
||||||
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
|
|||||||
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 {
|
|||||||
// 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 {
|
|||||||
{ 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 {
|
|||||||
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 {
|
|||||||
// 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 {
|
|||||||
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 {
|
|||||||
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
|
|||||||
// 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);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|||||||
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';
|
|||||||
|
|
||||||
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()', () => {
|
|||||||
|
|
||||||
// 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()', () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
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()', () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
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';
|
|||||||
*/
|
*/
|
||||||
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
|
|||||||
// 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
|
|||||||
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
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hints.length > 0 ? hints : null;
|
return hints;
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
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) {
|
|||||||
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) {
|
|||||||
|
|
||||||
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';
|
|||||||
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 {
|
|||||||
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', () => {
|
|||||||
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 {
|
|||||||
|
|
||||||
// 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
|
|||||||
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 {
|
|||||||
},
|
},
|
||||||
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 {
|
|||||||
},
|
},
|
||||||
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', () => {
|
|||||||
|
|
||||||
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(
|
|||||||
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(
|
|||||||
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) {
|
|||||||
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
|
|||||||
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';
|
|||||||
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 @@
|
|||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user