mirror of https://github.com/grafana/grafana
parent
406b6144a5
commit
b0172427b1
@ -0,0 +1,99 @@ |
||||
import _ from 'lodash'; |
||||
|
||||
export function getQueryHints(series: any[], datasource?: any): any[] { |
||||
const hints = series.map((s, i) => { |
||||
const query: string = s.query; |
||||
const index: number = s.responseIndex; |
||||
if (query === undefined || index === undefined) { |
||||
return null; |
||||
} |
||||
|
||||
// ..._bucket metric needs a histogram_quantile()
|
||||
const histogramMetric = query.trim().match(/^\w+_bucket$/); |
||||
if (histogramMetric) { |
||||
const label = 'Time series has buckets, you probably wanted a histogram.'; |
||||
return { |
||||
index, |
||||
label, |
||||
fix: { |
||||
label: 'Fix by adding histogram_quantile().', |
||||
action: { |
||||
type: 'ADD_HISTOGRAM_QUANTILE', |
||||
query, |
||||
index, |
||||
}, |
||||
}, |
||||
}; |
||||
} |
||||
|
||||
// Check for monotony
|
||||
const datapoints: number[][] = s.datapoints; |
||||
if (query.indexOf('rate(') === -1 && datapoints.length > 1) { |
||||
let increasing = false; |
||||
const monotonic = datapoints.filter(dp => dp[0] !== null).every((dp, index) => { |
||||
if (index === 0) { |
||||
return true; |
||||
} |
||||
increasing = increasing || dp[0] > datapoints[index - 1][0]; |
||||
// monotonic?
|
||||
return dp[0] >= datapoints[index - 1][0]; |
||||
}); |
||||
if (increasing && monotonic) { |
||||
const simpleMetric = query.trim().match(/^\w+$/); |
||||
let label = 'Time series is monotonously increasing.'; |
||||
let fix; |
||||
if (simpleMetric) { |
||||
fix = { |
||||
label: 'Fix by adding rate().', |
||||
action: { |
||||
type: 'ADD_RATE', |
||||
query, |
||||
index, |
||||
}, |
||||
}; |
||||
} else { |
||||
label = `${label} Try applying a rate() function.`; |
||||
} |
||||
return { |
||||
label, |
||||
index, |
||||
fix, |
||||
}; |
||||
} |
||||
} |
||||
|
||||
// Check for recording rules expansion
|
||||
if (datasource && datasource.ruleMappings) { |
||||
const mapping = datasource.ruleMappings; |
||||
const mappingForQuery = Object.keys(mapping).reduce((acc, ruleName) => { |
||||
if (query.search(ruleName) > -1) { |
||||
return { |
||||
...acc, |
||||
[ruleName]: mapping[ruleName], |
||||
}; |
||||
} |
||||
return acc; |
||||
}, {}); |
||||
if (_.size(mappingForQuery) > 0) { |
||||
const label = 'Query contains recording rules.'; |
||||
return { |
||||
label, |
||||
index, |
||||
fix: { |
||||
label: 'Expand rules', |
||||
action: { |
||||
type: 'EXPAND_RULES', |
||||
query, |
||||
index, |
||||
mapping: mappingForQuery, |
||||
}, |
||||
}, |
||||
}; |
||||
} |
||||
} |
||||
|
||||
// No hint found
|
||||
return null; |
||||
}); |
||||
return hints; |
||||
} |
||||
@ -0,0 +1,79 @@ |
||||
import { getQueryHints } from '../query_hints'; |
||||
|
||||
describe('getQueryHints()', () => { |
||||
it('returns no hints for no series', () => { |
||||
expect(getQueryHints([])).toEqual([]); |
||||
}); |
||||
|
||||
it('returns no hints for empty series', () => { |
||||
expect(getQueryHints([{ datapoints: [], query: '' }])).toEqual([null]); |
||||
}); |
||||
|
||||
it('returns no hint for a monotonously decreasing series', () => { |
||||
const series = [{ datapoints: [[23, 1000], [22, 1001]], query: 'metric', responseIndex: 0 }]; |
||||
const hints = getQueryHints(series); |
||||
expect(hints).toEqual([null]); |
||||
}); |
||||
|
||||
it('returns a rate hint for a monotonously increasing series', () => { |
||||
const series = [{ datapoints: [[23, 1000], [24, 1001]], query: 'metric', responseIndex: 0 }]; |
||||
const hints = getQueryHints(series); |
||||
expect(hints.length).toBe(1); |
||||
expect(hints[0]).toMatchObject({ |
||||
label: 'Time series is monotonously increasing.', |
||||
index: 0, |
||||
fix: { |
||||
action: { |
||||
type: 'ADD_RATE', |
||||
query: 'metric', |
||||
}, |
||||
}, |
||||
}); |
||||
}); |
||||
|
||||
it('returns no rate hint for a monotonously increasing series that already has a rate', () => { |
||||
const series = [{ datapoints: [[23, 1000], [24, 1001]], query: 'rate(metric[1m])', responseIndex: 0 }]; |
||||
const hints = getQueryHints(series); |
||||
expect(hints).toEqual([null]); |
||||
}); |
||||
|
||||
it('returns a rate hint w/o action for a complex monotonously increasing series', () => { |
||||
const series = [{ datapoints: [[23, 1000], [24, 1001]], query: 'sum(metric)', responseIndex: 0 }]; |
||||
const hints = getQueryHints(series); |
||||
expect(hints.length).toBe(1); |
||||
expect(hints[0].label).toContain('rate()'); |
||||
expect(hints[0].fix).toBeUndefined(); |
||||
}); |
||||
|
||||
it('returns a rate hint for a monotonously increasing series with missing data', () => { |
||||
const series = [{ datapoints: [[23, 1000], [null, 1001], [24, 1002]], query: 'metric', responseIndex: 0 }]; |
||||
const hints = getQueryHints(series); |
||||
expect(hints.length).toBe(1); |
||||
expect(hints[0]).toMatchObject({ |
||||
label: 'Time series is monotonously increasing.', |
||||
index: 0, |
||||
fix: { |
||||
action: { |
||||
type: 'ADD_RATE', |
||||
query: 'metric', |
||||
}, |
||||
}, |
||||
}); |
||||
}); |
||||
|
||||
it('returns a histogram hint for a bucket series', () => { |
||||
const series = [{ datapoints: [[23, 1000]], query: 'metric_bucket', responseIndex: 0 }]; |
||||
const hints = getQueryHints(series); |
||||
expect(hints.length).toBe(1); |
||||
expect(hints[0]).toMatchObject({ |
||||
label: 'Time series has buckets, you probably wanted a histogram.', |
||||
index: 0, |
||||
fix: { |
||||
action: { |
||||
type: 'ADD_HISTOGRAM_QUANTILE', |
||||
query: 'metric_bucket', |
||||
}, |
||||
}, |
||||
}); |
||||
}); |
||||
}); |
||||
Loading…
Reference in new issue