2022-03-29 22:23:08 -06:00
import { cloneDeep } from 'lodash' ;
2022-08-17 14:37:29 +02:00
import { lastValueFrom , of , throwError } from 'rxjs' ;
2022-04-22 14:33:13 +01:00
2019-12-02 18:14:26 +01:00
import {
2023-02-09 09:03:13 +00:00
AnnotationEvent ,
AnnotationQueryRequest ,
2020-02-05 16:40:37 +01:00
CoreApp ,
2019-12-02 18:14:26 +01:00
DataQueryRequest ,
2023-02-09 09:03:13 +00:00
DataQueryResponse ,
2020-02-05 16:40:37 +01:00
DataQueryResponseData ,
DataSourceInstanceSettings ,
2019-12-02 18:14:26 +01:00
dateTime ,
2022-03-29 22:23:08 -06:00
Field ,
2020-06-04 13:44:48 +02:00
getFieldDisplayName ,
2019-12-02 18:14:26 +01:00
LoadingState ,
Field: getFieldTitle as field / series display identity and use it in all field name matchers & field / series name displays (#24024)
* common title handling
* show labels
* update comment
* Update changelog for v7.0.0-beta1 (#24007)
Co-Authored-By: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-Authored-By: Andrej Ocenas <mr.ocenas@gmail.com>
Co-Authored-By: Hugo Häggmark <hugo.haggmark@grafana.com>
Co-authored-by: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com>
* verify-repo-update: Fix Dockerfile.deb (#24030)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* CircleCI: Upgrade build pipeline tool (#24021)
* CircleCI: Upgrade build pipeline tool
* Devenv: ignore enterprise (#24037)
* Add header icon to Add data source page (#24033)
* latest.json: Update testing version (#24038)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* Fix login page redirected from password reset (#24032)
* Storybook: Rewrite stories to CSF (#23989)
* ColorPicker to CSF format
* Convert stories to CSF
* Do not export ClipboardButton
* Update ConfirmButton
* Remove unused imports
* Fix feedback
* changelog enterprise 7.0.0-beta1 (#24039)
* CircleCI: Bump grafana/build-container revision (#24043)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* Changelog: Updates changelog with more feature details (#24040)
* Changelog: Updates changelog with more feature details
* spell fix
* spell fix
* Updates
* Readme update
* Updates
* Select: fixes so component loses focus on selecting value or pressing outside of input. (#24008)
* changed the value container to a class component to get it to work with focus (maybe something with context?).
* added e2e tests to verify that the select focus is working as it should.
* fixed according to feedback.
* updated snapshot.
* Devenv: add remote renderer to grafana (#24050)
* NewPanelEditor: minor UI twekas (#24042)
* Forward ref for tabs, use html props
* Inspect: add inspect label to drawer title
* Add tooltips to sidebar pane tabs, copy changes
* Remove unused import
* Place tooltips over tabs
* Inspector: dont show transformations select if there is only one data frame
* Review
* Changelog: Add a breaking change (#24051)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* CircleCI: Unpin grafana/docs-base (#24054)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* Search: close overlay on Esc press (#24003)
* Search: Close on Esc
* Search: Increase bottom padding for the last item in section
* Search: Move closing search to keybindingsSrv
* Search: Fix folder view
* Search: Do not move folders if already in folder
* Docs: Adds deprecation notice to changelog and docs for scripted dashboards (#24060)
* Update CHANGELOG.md (#24047)
Fix typo
Co-authored-by: Daniel Lee <dan.limerick@gmail.com>
* Documentation: Alternative Team Sync Wording (#23960)
* Alternative wording for team sync docs
Signed-off-by: Joe Elliott <number101010@gmail.com>
* Update docs/sources/auth/team-sync.md
Co-Authored-By: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
* Fix misspell issues (#23905)
* Fix misspell issues
See,
$ golangci-lint run --timeout 10m --disable-all -E misspell ./...
Signed-off-by: Mario Trangoni <mjtrangoni@gmail.com>
* Fix codespell issues
See,
$ codespell -S './.git*' -L 'uint,thru,pres,unknwon,serie,referer,uptodate,durationm'
Signed-off-by: Mario Trangoni <mjtrangoni@gmail.com>
* ci please?
* non-empty commit - ci?
* Trigger build
Co-authored-by: bergquist <carl.bergquist@gmail.com>
Co-authored-by: Kyle Brandt <kyle@grafana.com>
* fix compile error
* better series display
* better display
* now with prometheus and loki
* a few more tests
* Improvements and tests
* thinking
* More advanced and smart default title generation
* Another fix
* Progress but dam this will be hard
* Reverting the time series Value field name change
* revert revert going in circles
* add a field state object
* Use state title when converting back to legacy format
* Improved the join (series to columsn) transformer
* Got tests running again
* Rewrite of seriesToColums that simplifies and fixing tests
* Fixed the tricky problem of multiple time field when not used in join
* Prometheus: Restoring prometheus formatting
* Graphite: Disable Grafana's series naming
* fixed imports
* Fixed tests and made rename transform change title instead
* Fixing more tests
* fix more tests
* fixed import issue
* Fixed more circular dependencies
* Renamed to getFieldTitle
* More rename
* Review feedback
* Fix for showing field title in calculate field transformer
* fieldOverride: Make it clear that state title after applying defaults & overrides
* Fixed ts issue
* Update packages/grafana-ui/src/components/TransformersUI/OrganizeFieldsTransformerEditor.tsx
Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
Co-authored-by: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
Co-authored-by: Leonard Gram <leo@xlson.com>
Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
Co-authored-by: Alexander Zobnin <alexanderzobnin@gmail.com>
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
Co-authored-by: Richard Hartmann <RichiH@users.noreply.github.com>
Co-authored-by: Daniel Lee <dan.limerick@gmail.com>
Co-authored-by: Joe Elliott <joe.elliott@grafana.com>
Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
Co-authored-by: Mario Trangoni <mario@mariotrangoni.de>
Co-authored-by: bergquist <carl.bergquist@gmail.com>
Co-authored-by: Kyle Brandt <kyle@grafana.com>
2020-05-07 01:42:03 -07:00
toDataFrame ,
2019-12-02 18:14:26 +01:00
} from '@grafana/data' ;
2023-04-03 09:07:17 -05:00
import { config } from '@grafana/runtime' ;
2022-09-28 20:05:52 +03:00
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv' ;
import { TemplateSrv } from 'app/features/templating/template_srv' ;
2022-04-22 14:33:13 +01:00
import { QueryOptions } from 'app/types' ;
import { VariableHide } from '../../../features/variables/types' ;
2020-10-01 18:51:23 +01:00
import {
alignRange ,
extractRuleMappingFromGroups ,
PrometheusDatasource ,
prometheusRegularEscape ,
prometheusSpecialRegexEscape ,
} from './datasource' ;
2023-02-09 09:03:13 +00:00
import PromQlLanguageProvider from './language_provider' ;
2023-04-03 09:07:17 -05:00
import { PrometheusCacheLevel , PromOptions , PromQuery , PromQueryRequest } from './types' ;
2020-01-21 09:08:07 +00:00
2020-08-20 21:03:59 -07:00
const fetchMock = jest . fn ( ) . mockReturnValue ( of ( createDefaultPromResponse ( ) ) ) ;
2019-11-21 15:36:56 +00:00
2019-12-02 18:14:26 +01:00
jest . mock ( './metric_find_query' ) ;
2020-01-21 09:08:07 +00:00
jest . mock ( '@grafana/runtime' , ( ) = > ( {
2020-10-01 18:51:23 +01:00
. . . jest . requireActual ( '@grafana/runtime' ) ,
2020-01-21 09:08:07 +00:00
getBackendSrv : ( ) = > ( {
2020-08-20 21:03:59 -07:00
fetch : fetchMock ,
2020-01-21 09:08:07 +00:00
} ) ,
} ) ) ;
2023-02-09 09:03:13 +00:00
const getAdhocFiltersMock = jest . fn ( ) . mockImplementation ( ( ) = > [ ] ) ;
const replaceMock = jest . fn ( ) . mockImplementation ( ( a : string , . . . rest : unknown [ ] ) = > a ) ;
2020-10-01 18:51:23 +01:00
const templateSrvStub = {
2023-02-09 09:03:13 +00:00
getAdhocFilters : getAdhocFiltersMock ,
replace : replaceMock ,
} as unknown as TemplateSrv ;
2019-11-21 15:36:56 +00:00
2023-04-03 09:07:17 -05:00
const fromSeconds = 1674500289215 ;
const toSeconds = 1674500349215 ;
const timeSrvStubOld = {
2023-02-09 09:03:13 +00:00
timeRange() {
2020-10-01 18:51:23 +01:00
return {
from : dateTime ( 1531468681 ) ,
to : dateTime ( 1531489712 ) ,
} ;
} ,
2023-04-03 09:07:17 -05:00
} as TimeSrv ;
const timeSrvStub : TimeSrv = {
timeRange() {
return {
from : dateTime ( fromSeconds ) ,
to : dateTime ( toSeconds ) ,
} ;
} ,
} as TimeSrv ;
2019-11-21 15:36:56 +00:00
beforeEach ( ( ) = > {
2020-10-01 18:51:23 +01:00
jest . clearAllMocks ( ) ;
2019-11-21 15:36:56 +00:00
} ) ;
2019-09-13 15:08:29 +02:00
2019-12-02 18:14:26 +01:00
describe ( 'PrometheusDatasource' , ( ) = > {
let ds : PrometheusDatasource ;
2022-02-02 12:02:32 +00:00
const instanceSettings = {
2019-12-02 18:14:26 +01:00
url : 'proxied' ,
2022-06-03 15:56:13 -03:00
id : 1 ,
2023-02-06 11:02:02 +01:00
uid : 'ABCDEF' ,
2019-12-02 18:14:26 +01:00
directUrl : 'direct' ,
user : 'test' ,
password : 'mupp' ,
2020-11-26 11:56:14 +02:00
jsonData : {
customQueryParameters : '' ,
2023-04-03 09:07:17 -05:00
cacheLevel : PrometheusCacheLevel.Low ,
} as Partial < PromOptions > ,
2022-02-02 12:02:32 +00:00
} as unknown as DataSourceInstanceSettings < PromOptions > ;
2019-09-13 15:08:29 +02:00
2019-12-02 18:14:26 +01:00
beforeEach ( ( ) = > {
2023-02-09 09:03:13 +00:00
ds = new PrometheusDatasource ( instanceSettings , templateSrvStub , timeSrvStub ) ;
2019-12-02 18:14:26 +01:00
} ) ;
2019-09-13 15:08:29 +02:00
2023-04-13 15:05:20 -05:00
// Some functions are required by the parent datasource class to provide functionality such as ad-hoc filters, which requires the definition of the getTagKeys, and getTagValues functions
describe ( 'Datasource contract' , ( ) = > {
it ( 'has function called getTagKeys' , ( ) = > {
expect ( Object . getOwnPropertyNames ( Object . getPrototypeOf ( ds ) ) ) . toContain ( 'getTagKeys' ) ;
} ) ;
it ( 'has function called getTagValues' , ( ) = > {
expect ( Object . getOwnPropertyNames ( Object . getPrototypeOf ( ds ) ) ) . toContain ( 'getTagValues' ) ;
} ) ;
} ) ;
2019-12-02 18:14:26 +01:00
describe ( 'Query' , ( ) = > {
2021-01-15 16:20:20 +01:00
it ( 'returns empty array when no queries' , async ( ) = > {
2021-01-20 07:59:48 +01:00
await expect ( ds . query ( createDataRequest ( [ ] ) ) ) . toEmitValuesWith ( ( response ) = > {
2021-01-15 16:20:20 +01:00
expect ( response [ 0 ] . data ) . toEqual ( [ ] ) ;
expect ( response [ 0 ] . state ) . toBe ( LoadingState . Done ) ;
2019-09-13 15:08:29 +02:00
} ) ;
} ) ;
2021-01-15 16:20:20 +01:00
it ( 'performs time series queries' , async ( ) = > {
2021-01-20 07:59:48 +01:00
await expect ( ds . query ( createDataRequest ( [ { } ] ) ) ) . toEmitValuesWith ( ( response ) = > {
2021-01-15 16:20:20 +01:00
expect ( response [ 0 ] . data . length ) . not . toBe ( 0 ) ;
expect ( response [ 0 ] . state ) . toBe ( LoadingState . Done ) ;
2019-09-13 15:08:29 +02:00
} ) ;
} ) ;
2021-01-15 16:20:20 +01:00
it ( 'with 2 queries and used from Explore, sends results as they arrive' , async ( ) = > {
2021-01-20 07:59:48 +01:00
await expect ( ds . query ( createDataRequest ( [ { } , { } ] , { app : CoreApp.Explore } ) ) ) . toEmitValuesWith ( ( response ) = > {
2021-01-15 16:20:20 +01:00
expect ( response [ 0 ] . data . length ) . not . toBe ( 0 ) ;
expect ( response [ 0 ] . state ) . toBe ( LoadingState . Loading ) ;
expect ( response [ 1 ] . state ) . toBe ( LoadingState . Done ) ;
2019-09-13 15:08:29 +02:00
} ) ;
} ) ;
2019-10-03 05:15:59 -07:00
2021-01-15 16:20:20 +01:00
it ( 'with 2 queries and used from Panel, waits for all to finish until sending Done status' , async ( ) = > {
2021-01-20 07:59:48 +01:00
await expect ( ds . query ( createDataRequest ( [ { } , { } ] , { app : CoreApp.Dashboard } ) ) ) . toEmitValuesWith ( ( response ) = > {
2021-01-15 16:20:20 +01:00
expect ( response [ 0 ] . data . length ) . not . toBe ( 0 ) ;
expect ( response [ 0 ] . state ) . toBe ( LoadingState . Done ) ;
2019-10-03 05:15:59 -07:00
} ) ;
} ) ;
2022-08-17 14:37:29 +02:00
it ( 'throws if using direct access' , async ( ) = > {
const instanceSettings = {
url : 'proxied' ,
directUrl : 'direct' ,
user : 'test' ,
password : 'mupp' ,
access : 'direct' ,
jsonData : {
customQueryParameters : '' ,
2023-02-09 09:03:13 +00:00
} ,
2022-08-17 14:37:29 +02:00
} as unknown as DataSourceInstanceSettings < PromOptions > ;
const range = { from : time ( { seconds : 63 } ) , to : time ( { seconds : 183 } ) } ;
2023-02-09 09:03:13 +00:00
const directDs = new PrometheusDatasource ( instanceSettings , templateSrvStub , timeSrvStub ) ;
2022-08-17 14:37:29 +02:00
await expect (
lastValueFrom ( directDs . query ( createDataRequest ( [ { } , { } ] , { app : CoreApp.Dashboard } ) ) )
) . rejects . toMatchObject ( { message : expect.stringMatching ( 'Browser access' ) } ) ;
// Cannot test because some other tests need "./metric_find_query" to be mocked and that prevents this to be
// tested. Checked manually that this ends up with throwing
// await expect(directDs.metricFindQuery('label_names(foo)')).rejects.toBeDefined();
jest . spyOn ( console , 'error' ) . mockImplementation ( ( ) = > { } ) ;
await expect ( directDs . testDatasource ( ) ) . resolves . toMatchObject ( {
message : expect.stringMatching ( 'Browser access' ) ,
status : 'error' ,
} ) ;
await expect (
directDs . annotationQuery ( {
range : { . . . range , raw : range } ,
rangeRaw : range ,
// Should be DataModel but cannot import that here from the main app. Needs to be moved to package first.
dashboard : { } ,
annotation : {
expr : 'metric' ,
name : 'test' ,
enable : true ,
iconColor : '' ,
} ,
} )
) . rejects . toMatchObject ( {
message : expect.stringMatching ( 'Browser access' ) ,
} ) ;
2023-04-13 15:05:20 -05:00
await expect ( directDs . getTagKeys ( ) ) . rejects . toMatchObject ( {
2022-08-17 14:37:29 +02:00
message : expect.stringMatching ( 'Browser access' ) ,
} ) ;
await expect ( directDs . getTagValues ( ) ) . rejects . toMatchObject ( {
message : expect.stringMatching ( 'Browser access' ) ,
} ) ;
} ) ;
2019-09-13 15:08:29 +02:00
} ) ;
2019-12-02 18:14:26 +01:00
describe ( 'Datasource metadata requests' , ( ) = > {
it ( 'should perform a GET request with the default config' , ( ) = > {
2021-04-27 16:16:48 +02:00
ds . metadataRequest ( '/foo' , { bar : 'baz baz' , foo : 'foo' } ) ;
2020-08-20 21:03:59 -07:00
expect ( fetchMock . mock . calls . length ) . toBe ( 1 ) ;
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . method ) . toBe ( 'GET' ) ;
2021-04-27 16:16:48 +02:00
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . url ) . toContain ( 'bar=baz%20baz&foo=foo' ) ;
2019-12-02 18:14:26 +01:00
} ) ;
2021-02-23 16:31:03 +01:00
it ( 'should still perform a GET request with the DS HTTP method set to POST and not POST-friendly endpoint' , ( ) = > {
2021-04-21 08:38:00 +01:00
const postSettings = cloneDeep ( instanceSettings ) ;
2019-12-02 18:14:26 +01:00
postSettings . jsonData . httpMethod = 'POST' ;
2023-02-09 09:03:13 +00:00
const promDs = new PrometheusDatasource ( postSettings , templateSrvStub , timeSrvStub ) ;
2019-12-02 18:14:26 +01:00
promDs . metadataRequest ( '/foo' ) ;
2020-08-20 21:03:59 -07:00
expect ( fetchMock . mock . calls . length ) . toBe ( 1 ) ;
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . method ) . toBe ( 'GET' ) ;
2019-12-02 18:14:26 +01:00
} ) ;
2021-02-23 16:31:03 +01:00
it ( 'should try to perform a POST request with the DS HTTP method set to POST and POST-friendly endpoint' , ( ) = > {
2021-04-21 08:38:00 +01:00
const postSettings = cloneDeep ( instanceSettings ) ;
2021-02-23 16:31:03 +01:00
postSettings . jsonData . httpMethod = 'POST' ;
2023-02-09 09:03:13 +00:00
const promDs = new PrometheusDatasource ( postSettings , templateSrvStub , timeSrvStub ) ;
2021-04-27 16:16:48 +02:00
promDs . metadataRequest ( 'api/v1/series' , { bar : 'baz baz' , foo : 'foo' } ) ;
2021-02-23 16:31:03 +01:00
expect ( fetchMock . mock . calls . length ) . toBe ( 1 ) ;
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . method ) . toBe ( 'POST' ) ;
2021-04-27 16:16:48 +02:00
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . url ) . not . toContain ( 'bar=baz%20baz&foo=foo' ) ;
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . data ) . toEqual ( { bar : 'baz baz' , foo : 'foo' } ) ;
2021-02-23 16:31:03 +01:00
} ) ;
2019-12-02 18:14:26 +01:00
} ) ;
2021-05-12 19:30:41 +02:00
describe ( 'customQueryParams' , ( ) = > {
2023-03-09 11:26:15 +01:00
const target : PromQuery = { expr : 'test{job="testjob"}' , format : 'time_series' , refId : '' } ;
2023-02-22 14:47:50 +01:00
2021-05-12 19:30:41 +02:00
function makeQuery ( target : PromQuery ) {
return {
range : { from : time ( { seconds : 63 } ) , to : time ( { seconds : 183 } ) } ,
targets : [ target ] ,
interval : '60s' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2021-05-12 19:30:41 +02:00
}
2021-05-25 18:56:46 +02:00
describe ( 'with GET http method' , ( ) = > {
const promDs = new PrometheusDatasource (
2023-02-09 09:03:13 +00:00
{ . . . instanceSettings , jsonData : { customQueryParameters : 'customQuery=123' , httpMethod : 'GET' } } ,
templateSrvStub ,
timeSrvStub
2020-11-26 11:56:14 +02:00
) ;
2021-05-25 18:56:46 +02:00
it ( 'added to metadata request' , ( ) = > {
promDs . metadataRequest ( '/foo' ) ;
expect ( fetchMock . mock . calls . length ) . toBe ( 1 ) ;
2023-02-06 11:02:02 +01:00
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . url ) . toBe ( '/api/datasources/uid/ABCDEF/resources/foo?customQuery=123' ) ;
2021-05-25 18:56:46 +02:00
} ) ;
it ( 'adds params to timeseries query' , ( ) = > {
promDs . query ( makeQuery ( target ) ) ;
expect ( fetchMock . mock . calls . length ) . toBe ( 1 ) ;
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . url ) . toBe (
'proxied/api/v1/query_range?query=test%7Bjob%3D%22testjob%22%7D&start=60&end=180&step=60&customQuery=123'
) ;
} ) ;
it ( 'adds params to exemplars query' , ( ) = > {
promDs . query ( makeQuery ( { . . . target , exemplar : true } ) ) ;
// We do also range query for single exemplars target
expect ( fetchMock . mock . calls . length ) . toBe ( 2 ) ;
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . url ) . toContain ( '&customQuery=123' ) ;
expect ( fetchMock . mock . calls [ 1 ] [ 0 ] . url ) . toContain ( '&customQuery=123' ) ;
} ) ;
it ( 'adds params to instant query' , ( ) = > {
promDs . query ( makeQuery ( { . . . target , instant : true } ) ) ;
expect ( fetchMock . mock . calls . length ) . toBe ( 1 ) ;
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . url ) . toContain ( '&customQuery=123' ) ;
} ) ;
2021-05-12 19:30:41 +02:00
} ) ;
2021-05-25 18:56:46 +02:00
describe ( 'with POST http method' , ( ) = > {
const promDs = new PrometheusDatasource (
2023-02-09 09:03:13 +00:00
{ . . . instanceSettings , jsonData : { customQueryParameters : 'customQuery=123' , httpMethod : 'POST' } } ,
templateSrvStub ,
timeSrvStub
2021-05-25 18:56:46 +02:00
) ;
it ( 'added to metadata request with non-POST endpoint' , ( ) = > {
promDs . metadataRequest ( '/foo' ) ;
expect ( fetchMock . mock . calls . length ) . toBe ( 1 ) ;
2023-02-06 11:02:02 +01:00
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . url ) . toBe ( '/api/datasources/uid/ABCDEF/resources/foo?customQuery=123' ) ;
2021-05-25 18:56:46 +02:00
} ) ;
it ( 'added to metadata request with POST endpoint' , ( ) = > {
promDs . metadataRequest ( '/api/v1/labels' ) ;
expect ( fetchMock . mock . calls . length ) . toBe ( 1 ) ;
2023-02-06 11:02:02 +01:00
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . url ) . toBe ( '/api/datasources/uid/ABCDEF/resources/api/v1/labels' ) ;
2021-05-25 18:56:46 +02:00
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . data . customQuery ) . toBe ( '123' ) ;
} ) ;
it ( 'adds params to timeseries query' , ( ) = > {
promDs . query ( makeQuery ( target ) ) ;
expect ( fetchMock . mock . calls . length ) . toBe ( 1 ) ;
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . url ) . toBe ( 'proxied/api/v1/query_range' ) ;
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . data ) . toEqual ( {
customQuery : '123' ,
query : 'test{job="testjob"}' ,
step : 60 ,
end : 180 ,
start : 60 ,
} ) ;
} ) ;
it ( 'adds params to exemplars query' , ( ) = > {
promDs . query ( makeQuery ( { . . . target , exemplar : true } ) ) ;
// We do also range query for single exemplars target
expect ( fetchMock . mock . calls . length ) . toBe ( 2 ) ;
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . data . customQuery ) . toBe ( '123' ) ;
expect ( fetchMock . mock . calls [ 1 ] [ 0 ] . data . customQuery ) . toBe ( '123' ) ;
} ) ;
it ( 'adds params to instant query' , ( ) = > {
promDs . query ( makeQuery ( { . . . target , instant : true } ) ) ;
expect ( fetchMock . mock . calls . length ) . toBe ( 1 ) ;
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . data . customQuery ) . toBe ( '123' ) ;
} ) ;
2021-05-12 19:30:41 +02:00
} ) ;
2020-11-26 11:56:14 +02:00
} ) ;
2019-12-02 18:14:26 +01:00
describe ( 'When using adhoc filters' , ( ) = > {
const DEFAULT_QUERY_EXPRESSION = 'metric{job="foo"} - metric' ;
2023-02-09 09:03:13 +00:00
const target : PromQuery = { expr : DEFAULT_QUERY_EXPRESSION , refId : 'A' } ;
2019-12-02 18:14:26 +01:00
afterAll ( ( ) = > {
2023-02-09 09:03:13 +00:00
getAdhocFiltersMock . mockImplementation ( ( ) = > [ ] ) ;
2019-12-02 18:14:26 +01:00
} ) ;
it ( 'should not modify expression with no filters' , ( ) = > {
2023-02-09 09:03:13 +00:00
const result = ds . createQuery ( target , { interval : '15s' } as DataQueryRequest < PromQuery > , 0 , 0 ) ;
2019-12-02 18:14:26 +01:00
expect ( result ) . toMatchObject ( { expr : DEFAULT_QUERY_EXPRESSION } ) ;
} ) ;
it ( 'should add filters to expression' , ( ) = > {
2023-02-09 09:03:13 +00:00
getAdhocFiltersMock . mockReturnValue ( [
2019-12-02 18:14:26 +01:00
{
key : 'k1' ,
operator : '=' ,
value : 'v1' ,
} ,
{
key : 'k2' ,
operator : '!=' ,
value : 'v2' ,
} ,
] ) ;
2023-02-09 09:03:13 +00:00
const result = ds . createQuery ( target , { interval : '15s' } as DataQueryRequest < PromQuery > , 0 , 0 ) ;
2022-03-15 17:37:20 +01:00
expect ( result ) . toMatchObject ( { expr : 'metric{job="foo", k1="v1", k2!="v2"} - metric{k1="v1", k2!="v2"}' } ) ;
2019-12-02 18:14:26 +01:00
} ) ;
it ( 'should add escaping if needed to regex filter expressions' , ( ) = > {
2023-02-09 09:03:13 +00:00
getAdhocFiltersMock . mockReturnValue ( [
2019-12-02 18:14:26 +01:00
{
key : 'k1' ,
operator : '=~' ,
value : 'v.*' ,
} ,
{
key : 'k2' ,
operator : '=~' ,
value : ` v'.* ` ,
} ,
] ) ;
2023-02-09 09:03:13 +00:00
const result = ds . createQuery ( target , { interval : '15s' } as DataQueryRequest < PromQuery > , 0 , 0 ) ;
2019-12-02 18:14:26 +01:00
expect ( result ) . toMatchObject ( {
2022-03-15 17:37:20 +01:00
expr : ` metric{job="foo", k1=~"v.*", k2=~"v \\ \\ '.*"} - metric{k1=~"v.*", k2=~"v \\ \\ '.*"} ` ,
2019-12-02 18:14:26 +01:00
} ) ;
} ) ;
} ) ;
describe ( 'When converting prometheus histogram to heatmap format' , ( ) = > {
2023-02-09 09:03:13 +00:00
let query : DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
beforeEach ( ( ) = > {
query = {
range : { from : dateTime ( 1443454528000 ) , to : dateTime ( 1443454528000 ) } ,
targets : [ { expr : 'test{job="testjob"}' , format : 'heatmap' , legendFormat : '{{le}}' } ] ,
interval : '1s' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
} ) ;
2021-01-15 16:20:20 +01:00
it ( 'should convert cumulative histogram to ordinary' , async ( ) = > {
2019-12-02 18:14:26 +01:00
const resultMock = [
{
metric : { __name__ : 'metric' , job : 'testjob' , le : '10' } ,
values : [
[ 1443454528.0 , '10' ] ,
[ 1443454528.0 , '10' ] ,
] ,
} ,
{
metric : { __name__ : 'metric' , job : 'testjob' , le : '20' } ,
values : [
[ 1443454528.0 , '20' ] ,
[ 1443454528.0 , '10' ] ,
] ,
} ,
{
metric : { __name__ : 'metric' , job : 'testjob' , le : '30' } ,
values : [
[ 1443454528.0 , '25' ] ,
[ 1443454528.0 , '10' ] ,
] ,
} ,
] ;
const responseMock = { data : { data : { result : resultMock } } } ;
2021-01-15 16:20:20 +01:00
ds . performTimeSeriesQuery = jest . fn ( ) . mockReturnValue ( of ( responseMock ) ) ;
2021-01-20 07:59:48 +01:00
await expect ( ds . query ( query ) ) . toEmitValuesWith ( ( result ) = > {
2021-01-15 16:20:20 +01:00
const results = result [ 0 ] . data ;
expect ( results [ 0 ] . fields [ 1 ] . values . toArray ( ) ) . toEqual ( [ 10 , 10 ] ) ;
2022-03-29 22:23:08 -06:00
expect ( results [ 0 ] . fields [ 2 ] . values . toArray ( ) ) . toEqual ( [ 10 , 0 ] ) ;
expect ( results [ 0 ] . fields [ 3 ] . values . toArray ( ) ) . toEqual ( [ 5 , 0 ] ) ;
2019-12-02 18:14:26 +01:00
} ) ;
} ) ;
2021-01-15 16:20:20 +01:00
it ( 'should sort series by label value' , async ( ) = > {
2019-12-02 18:14:26 +01:00
const resultMock = [
{
metric : { __name__ : 'metric' , job : 'testjob' , le : '2' } ,
values : [
[ 1443454528.0 , '10' ] ,
[ 1443454528.0 , '10' ] ,
] ,
} ,
{
metric : { __name__ : 'metric' , job : 'testjob' , le : '4' } ,
values : [
[ 1443454528.0 , '20' ] ,
[ 1443454528.0 , '10' ] ,
] ,
} ,
{
metric : { __name__ : 'metric' , job : 'testjob' , le : '+Inf' } ,
values : [
[ 1443454528.0 , '25' ] ,
[ 1443454528.0 , '10' ] ,
] ,
} ,
{
metric : { __name__ : 'metric' , job : 'testjob' , le : '1' } ,
values : [
[ 1443454528.0 , '25' ] ,
[ 1443454528.0 , '10' ] ,
] ,
} ,
] ;
const responseMock = { data : { data : { result : resultMock } } } ;
const expected = [ '1' , '2' , '4' , '+Inf' ] ;
2021-01-15 16:20:20 +01:00
ds . performTimeSeriesQuery = jest . fn ( ) . mockReturnValue ( of ( responseMock ) ) ;
2021-01-20 07:59:48 +01:00
await expect ( ds . query ( query ) ) . toEmitValuesWith ( ( result ) = > {
2022-03-29 22:23:08 -06:00
const seriesLabels = result [ 0 ] . data [ 0 ] . fields . slice ( 1 ) . map ( ( field : Field ) = > getFieldDisplayName ( field ) ) ;
2021-01-15 16:20:20 +01:00
expect ( seriesLabels ) . toEqual ( expected ) ;
2019-12-02 18:14:26 +01:00
} ) ;
} ) ;
} ) ;
2023-04-03 09:07:17 -05:00
// Remove when prometheusResourceBrowserCache is removed
describe ( 'When prometheusResourceBrowserCache feature flag is off, there should be no change to the query intervals ' , ( ) = > {
beforeEach ( ( ) = > {
config . featureToggles . prometheusResourceBrowserCache = false ;
} ) ;
it ( 'test default 1 minute quantization' , ( ) = > {
const dataSource = new PrometheusDatasource (
{
. . . instanceSettings ,
jsonData : { . . . instanceSettings . jsonData , cacheLevel : PrometheusCacheLevel.Low } ,
} ,
templateSrvStub as unknown as TemplateSrv ,
timeSrvStub as unknown as TimeSrv
) ;
const quantizedRange = dataSource . getAdjustedInterval ( ) ;
const oldRange = dataSource . getTimeRangeParams ( ) ;
// For "1 minute" the window is unchanged
expect ( parseInt ( quantizedRange . end , 10 ) - parseInt ( quantizedRange . start , 10 ) ) . toBe ( 60 ) ;
expect ( parseInt ( oldRange . end , 10 ) - parseInt ( oldRange . start , 10 ) ) . toBe ( 60 ) ;
} ) ;
it ( 'test 10 minute quantization' , ( ) = > {
const dataSource = new PrometheusDatasource (
{
. . . instanceSettings ,
jsonData : { . . . instanceSettings . jsonData , cacheLevel : PrometheusCacheLevel.Medium } ,
} ,
templateSrvStub as unknown as TemplateSrv ,
timeSrvStub as unknown as TimeSrv
) ;
const quantizedRange = dataSource . getAdjustedInterval ( ) ;
const oldRange = dataSource . getTimeRangeParams ( ) ;
expect ( parseInt ( quantizedRange . end , 10 ) - parseInt ( quantizedRange . start , 10 ) ) . toBe ( 60 ) ;
expect ( parseInt ( oldRange . end , 10 ) - parseInt ( oldRange . start , 10 ) ) . toBe ( 60 ) ;
} ) ;
} ) ;
describe ( 'Test query range snapping' , ( ) = > {
beforeEach ( ( ) = > {
config . featureToggles . prometheusResourceBrowserCache = true ;
} ) ;
it ( 'test default 1 minute quantization' , ( ) = > {
const dataSource = new PrometheusDatasource (
{
. . . instanceSettings ,
jsonData : { . . . instanceSettings . jsonData , cacheLevel : PrometheusCacheLevel.Low } ,
} ,
templateSrvStub as unknown as TemplateSrv ,
timeSrvStub as unknown as TimeSrv
) ;
const quantizedRange = dataSource . getAdjustedInterval ( ) ;
// For "1 minute" the window contains all the minutes, so a query from 1:11:09 - 1:12:09 becomes 1:11 - 1:13
expect ( parseInt ( quantizedRange . end , 10 ) - parseInt ( quantizedRange . start , 10 ) ) . toBe ( 120 ) ;
} ) ;
it ( 'test 10 minute quantization' , ( ) = > {
const dataSource = new PrometheusDatasource (
{
. . . instanceSettings ,
jsonData : { . . . instanceSettings . jsonData , cacheLevel : PrometheusCacheLevel.Medium } ,
} ,
templateSrvStub as unknown as TemplateSrv ,
timeSrvStub as unknown as TimeSrv
) ;
const quantizedRange = dataSource . getAdjustedInterval ( ) ;
expect ( parseInt ( quantizedRange . end , 10 ) - parseInt ( quantizedRange . start , 10 ) ) . toBe ( 600 ) ;
} ) ;
it ( 'test 60 minute quantization' , ( ) = > {
const dataSource = new PrometheusDatasource (
{
. . . instanceSettings ,
jsonData : { . . . instanceSettings . jsonData , cacheLevel : PrometheusCacheLevel.High } ,
} ,
templateSrvStub as unknown as TemplateSrv ,
timeSrvStub as unknown as TimeSrv
) ;
const quantizedRange = dataSource . getAdjustedInterval ( ) ;
expect ( parseInt ( quantizedRange . end , 10 ) - parseInt ( quantizedRange . start , 10 ) ) . toBe ( 3600 ) ;
} ) ;
it ( 'test quantization turned off' , ( ) = > {
const dataSource = new PrometheusDatasource (
{
. . . instanceSettings ,
jsonData : { . . . instanceSettings . jsonData , cacheLevel : PrometheusCacheLevel.None } ,
} ,
templateSrvStub as unknown as TemplateSrv ,
timeSrvStub as unknown as TimeSrv
) ;
const quantizedRange = dataSource . getAdjustedInterval ( ) ;
expect ( parseInt ( quantizedRange . end , 10 ) - parseInt ( quantizedRange . start , 10 ) ) . toBe (
( toSeconds - fromSeconds ) / 1000
) ;
} ) ;
} ) ;
2019-12-02 18:14:26 +01:00
describe ( 'alignRange' , ( ) = > {
it ( 'does not modify already aligned intervals with perfect step' , ( ) = > {
const range = alignRange ( 0 , 3 , 3 , 0 ) ;
expect ( range . start ) . toEqual ( 0 ) ;
expect ( range . end ) . toEqual ( 3 ) ;
} ) ;
it ( 'does modify end-aligned intervals to reflect number of steps possible' , ( ) = > {
const range = alignRange ( 1 , 6 , 3 , 0 ) ;
expect ( range . start ) . toEqual ( 0 ) ;
expect ( range . end ) . toEqual ( 6 ) ;
} ) ;
it ( 'does align intervals that are a multiple of steps' , ( ) = > {
const range = alignRange ( 1 , 4 , 3 , 0 ) ;
expect ( range . start ) . toEqual ( 0 ) ;
expect ( range . end ) . toEqual ( 3 ) ;
} ) ;
it ( 'does align intervals that are not a multiple of steps' , ( ) = > {
const range = alignRange ( 1 , 5 , 3 , 0 ) ;
expect ( range . start ) . toEqual ( 0 ) ;
expect ( range . end ) . toEqual ( 3 ) ;
} ) ;
it ( 'does align intervals with local midnight -UTC offset' , ( ) = > {
//week range, location 4+ hours UTC offset, 24h step time
const range = alignRange ( 4 * 60 * 60 , ( 7 * 24 + 4 ) * 60 * 60 , 24 * 60 * 60 , - 4 * 60 * 60 ) ; //04:00 UTC, 7 day range
expect ( range . start ) . toEqual ( 4 * 60 * 60 ) ;
expect ( range . end ) . toEqual ( ( 7 * 24 + 4 ) * 60 * 60 ) ;
} ) ;
it ( 'does align intervals with local midnight +UTC offset' , ( ) = > {
//week range, location 4- hours UTC offset, 24h step time
const range = alignRange ( 20 * 60 * 60 , ( 8 * 24 - 4 ) * 60 * 60 , 24 * 60 * 60 , 4 * 60 * 60 ) ; //20:00 UTC on day1, 7 days later is 20:00 on day8
expect ( range . start ) . toEqual ( 20 * 60 * 60 ) ;
expect ( range . end ) . toEqual ( ( 8 * 24 - 4 ) * 60 * 60 ) ;
} ) ;
} ) ;
describe ( 'extractRuleMappingFromGroups()' , ( ) = > {
it ( 'returns empty mapping for no rule groups' , ( ) = > {
expect ( extractRuleMappingFromGroups ( [ ] ) ) . toEqual ( { } ) ;
} ) ;
it ( 'returns a mapping for recording rules only' , ( ) = > {
const groups = [
{
rules : [
{
name : 'HighRequestLatency' ,
query : 'job:request_latency_seconds:mean5m{job="myjob"} > 0.5' ,
type : 'alerting' ,
} ,
{
name : 'job:http_inprogress_requests:sum' ,
query : 'sum(http_inprogress_requests) by (job)' ,
type : 'recording' ,
} ,
] ,
file : '/rules.yaml' ,
interval : 60 ,
name : 'example' ,
} ,
] ;
const mapping = extractRuleMappingFromGroups ( groups ) ;
expect ( mapping ) . toEqual ( { 'job:http_inprogress_requests:sum' : 'sum(http_inprogress_requests) by (job)' } ) ;
} ) ;
} ) ;
2022-06-03 15:56:13 -03:00
describe ( 'Prometheus regular escaping' , ( ) = > {
2019-12-02 18:14:26 +01:00
it ( 'should not escape non-string' , ( ) = > {
expect ( prometheusRegularEscape ( 12 ) ) . toEqual ( 12 ) ;
} ) ;
it ( 'should not escape simple string' , ( ) = > {
expect ( prometheusRegularEscape ( 'cryptodepression' ) ) . toEqual ( 'cryptodepression' ) ;
} ) ;
it ( "should escape '" , ( ) = > {
expect ( prometheusRegularEscape ( "looking'glass" ) ) . toEqual ( "looking\\\\'glass" ) ;
} ) ;
2020-07-30 18:07:22 +02:00
it ( 'should escape \\' , ( ) = > {
expect ( prometheusRegularEscape ( 'looking\\glass' ) ) . toEqual ( 'looking\\\\glass' ) ;
} ) ;
2019-12-02 18:14:26 +01:00
it ( 'should escape multiple characters' , ( ) = > {
expect ( prometheusRegularEscape ( "'looking'glass'" ) ) . toEqual ( "\\\\'looking\\\\'glass\\\\'" ) ;
} ) ;
2020-07-30 18:07:22 +02:00
it ( 'should escape multiple different characters' , ( ) = > {
expect ( prometheusRegularEscape ( "'loo\\king'glass'" ) ) . toEqual ( "\\\\'loo\\\\king\\\\'glass\\\\'" ) ;
} ) ;
2019-12-02 18:14:26 +01:00
} ) ;
2022-06-03 15:56:13 -03:00
describe ( 'Prometheus regexes escaping' , ( ) = > {
2019-12-02 18:14:26 +01:00
it ( 'should not escape simple string' , ( ) = > {
expect ( prometheusSpecialRegexEscape ( 'cryptodepression' ) ) . toEqual ( 'cryptodepression' ) ;
} ) ;
it ( 'should escape $^*+?.()|\\' , ( ) = > {
expect ( prometheusSpecialRegexEscape ( "looking'glass" ) ) . toEqual ( "looking\\\\'glass" ) ;
expect ( prometheusSpecialRegexEscape ( 'looking{glass' ) ) . toEqual ( 'looking\\\\{glass' ) ;
expect ( prometheusSpecialRegexEscape ( 'looking}glass' ) ) . toEqual ( 'looking\\\\}glass' ) ;
expect ( prometheusSpecialRegexEscape ( 'looking[glass' ) ) . toEqual ( 'looking\\\\[glass' ) ;
expect ( prometheusSpecialRegexEscape ( 'looking]glass' ) ) . toEqual ( 'looking\\\\]glass' ) ;
expect ( prometheusSpecialRegexEscape ( 'looking$glass' ) ) . toEqual ( 'looking\\\\$glass' ) ;
expect ( prometheusSpecialRegexEscape ( 'looking^glass' ) ) . toEqual ( 'looking\\\\^glass' ) ;
expect ( prometheusSpecialRegexEscape ( 'looking*glass' ) ) . toEqual ( 'looking\\\\*glass' ) ;
expect ( prometheusSpecialRegexEscape ( 'looking+glass' ) ) . toEqual ( 'looking\\\\+glass' ) ;
expect ( prometheusSpecialRegexEscape ( 'looking?glass' ) ) . toEqual ( 'looking\\\\?glass' ) ;
expect ( prometheusSpecialRegexEscape ( 'looking.glass' ) ) . toEqual ( 'looking\\\\.glass' ) ;
expect ( prometheusSpecialRegexEscape ( 'looking(glass' ) ) . toEqual ( 'looking\\\\(glass' ) ;
expect ( prometheusSpecialRegexEscape ( 'looking)glass' ) ) . toEqual ( 'looking\\\\)glass' ) ;
expect ( prometheusSpecialRegexEscape ( 'looking\\glass' ) ) . toEqual ( 'looking\\\\\\\\glass' ) ;
expect ( prometheusSpecialRegexEscape ( 'looking|glass' ) ) . toEqual ( 'looking\\\\|glass' ) ;
} ) ;
it ( 'should escape multiple special characters' , ( ) = > {
expect ( prometheusSpecialRegexEscape ( '+looking$glass?' ) ) . toEqual ( '\\\\+looking\\\\$glass\\\\?' ) ;
} ) ;
} ) ;
describe ( 'When interpolating variables' , ( ) = > {
2020-06-04 13:44:48 +02:00
let customVariable : any ;
2019-12-02 18:14:26 +01:00
beforeEach ( ( ) = > {
2020-06-04 13:44:48 +02:00
customVariable = {
id : '' ,
global : false ,
multi : false ,
includeAll : false ,
allValue : null ,
query : '' ,
options : [ ] ,
current : { } ,
name : '' ,
type : 'custom' ,
label : null ,
hide : VariableHide.dontHide ,
skipUrlSync : false ,
index : - 1 ,
initLock : null ,
} ;
2019-12-02 18:14:26 +01:00
} ) ;
describe ( 'and value is a string' , ( ) = > {
it ( 'should only escape single quotes' , ( ) = > {
expect ( ds . interpolateQueryExpr ( "abc'$^*{}[]+?.()|" , customVariable ) ) . toEqual ( "abc\\\\'$^*{}[]+?.()|" ) ;
} ) ;
} ) ;
describe ( 'and value is a number' , ( ) = > {
it ( 'should return a number' , ( ) = > {
expect ( ds . interpolateQueryExpr ( 1000 as any , customVariable ) ) . toEqual ( 1000 ) ;
} ) ;
} ) ;
describe ( 'and variable allows multi-value' , ( ) = > {
beforeEach ( ( ) = > {
customVariable . multi = true ;
} ) ;
it ( 'should regex escape values if the value is a string' , ( ) = > {
expect ( ds . interpolateQueryExpr ( 'looking*glass' , customVariable ) ) . toEqual ( 'looking\\\\*glass' ) ;
} ) ;
it ( 'should return pipe separated values if the value is an array of strings' , ( ) = > {
2020-08-10 15:36:15 +02:00
expect ( ds . interpolateQueryExpr ( [ 'a|bc' , 'de|f' ] , customVariable ) ) . toEqual ( '(a\\\\|bc|de\\\\|f)' ) ;
2019-12-02 18:14:26 +01:00
} ) ;
2020-08-13 19:15:34 +02:00
it ( 'should return 1 regex escaped value if there is just 1 value in an array of strings' , ( ) = > {
expect ( ds . interpolateQueryExpr ( [ 'looking*glass' ] , customVariable ) ) . toEqual ( 'looking\\\\*glass' ) ;
} ) ;
2019-12-02 18:14:26 +01:00
} ) ;
describe ( 'and variable allows all' , ( ) = > {
beforeEach ( ( ) = > {
customVariable . includeAll = true ;
} ) ;
it ( 'should regex escape values if the array is a string' , ( ) = > {
expect ( ds . interpolateQueryExpr ( 'looking*glass' , customVariable ) ) . toEqual ( 'looking\\\\*glass' ) ;
} ) ;
it ( 'should return pipe separated values if the value is an array of strings' , ( ) = > {
2020-08-10 15:36:15 +02:00
expect ( ds . interpolateQueryExpr ( [ 'a|bc' , 'de|f' ] , customVariable ) ) . toEqual ( '(a\\\\|bc|de\\\\|f)' ) ;
2019-12-02 18:14:26 +01:00
} ) ;
2020-08-13 19:15:34 +02:00
it ( 'should return 1 regex escaped value if there is just 1 value in an array of strings' , ( ) = > {
expect ( ds . interpolateQueryExpr ( [ 'looking*glass' ] , customVariable ) ) . toEqual ( 'looking\\\\*glass' ) ;
} ) ;
2019-12-02 18:14:26 +01:00
} ) ;
} ) ;
2021-07-07 08:43:46 +02:00
describe ( 'interpolateVariablesInQueries' , ( ) = > {
it ( 'should call replace function 2 times' , ( ) = > {
2023-03-09 11:26:15 +01:00
const query : PromQuery = {
2021-07-07 08:43:46 +02:00
expr : 'test{job="testjob"}' ,
format : 'time_series' ,
interval : '$Interval' ,
refId : 'A' ,
} ;
const interval = '10m' ;
2023-02-09 09:03:13 +00:00
replaceMock . mockReturnValue ( interval ) ;
2021-07-07 08:43:46 +02:00
const queries = ds . interpolateVariablesInQueries ( [ query ] , { Interval : { text : interval , value : interval } } ) ;
expect ( templateSrvStub . replace ) . toBeCalledTimes ( 2 ) ;
expect ( queries [ 0 ] . interval ) . toBe ( interval ) ;
} ) ;
2022-09-29 11:17:41 +02:00
it ( 'should call enhanceExprWithAdHocFilters' , ( ) = > {
ds . enhanceExprWithAdHocFilters = jest . fn ( ) ;
const queries = [
{
refId : 'A' ,
expr : 'rate({bar="baz", job="foo"} [5m]' ,
} ,
] ;
ds . interpolateVariablesInQueries ( queries , { } ) ;
expect ( ds . enhanceExprWithAdHocFilters ) . toHaveBeenCalled ( ) ;
} ) ;
2021-07-07 08:43:46 +02:00
} ) ;
2021-10-22 09:58:59 +02:00
describe ( 'applyTemplateVariables' , ( ) = > {
2021-11-15 17:11:21 +01:00
afterAll ( ( ) = > {
2023-02-09 09:03:13 +00:00
getAdhocFiltersMock . mockImplementation ( ( ) = > [ ] ) ;
replaceMock . mockImplementation ( ( a : string , . . . rest : unknown [ ] ) = > a ) ;
2021-11-15 17:11:21 +01:00
} ) ;
2021-10-22 09:58:59 +02:00
it ( 'should call replace function for legendFormat' , ( ) = > {
const query = {
expr : 'test{job="bar"}' ,
legendFormat : '$legend' ,
refId : 'A' ,
} ;
const legend = 'baz' ;
2023-02-09 09:03:13 +00:00
replaceMock . mockReturnValue ( legend ) ;
2021-10-22 09:58:59 +02:00
const interpolatedQuery = ds . applyTemplateVariables ( query , { legend : { text : legend , value : legend } } ) ;
expect ( interpolatedQuery . legendFormat ) . toBe ( legend ) ;
} ) ;
2021-12-02 13:32:02 +01:00
it ( 'should call replace function for interval' , ( ) = > {
2021-10-22 09:58:59 +02:00
const query = {
2021-12-02 13:32:02 +01:00
expr : 'test{job="bar"}' ,
interval : '$step' ,
2021-10-22 09:58:59 +02:00
refId : 'A' ,
} ;
2021-12-02 13:32:02 +01:00
const step = '5s' ;
2023-02-09 09:03:13 +00:00
replaceMock . mockReturnValue ( step ) ;
2021-10-22 09:58:59 +02:00
2021-12-02 13:32:02 +01:00
const interpolatedQuery = ds . applyTemplateVariables ( query , { step : { text : step , value : step } } ) ;
expect ( interpolatedQuery . interval ) . toBe ( step ) ;
2021-10-22 09:58:59 +02:00
} ) ;
2021-12-02 13:32:02 +01:00
it ( 'should call replace function for expr' , ( ) = > {
2021-10-22 09:58:59 +02:00
const query = {
2021-12-02 13:32:02 +01:00
expr : 'test{job="$job"}' ,
2021-10-22 09:58:59 +02:00
refId : 'A' ,
} ;
2021-12-02 13:32:02 +01:00
const job = 'bar' ;
2023-02-09 09:03:13 +00:00
replaceMock . mockReturnValue ( job ) ;
2021-10-22 09:58:59 +02:00
2021-12-02 13:32:02 +01:00
const interpolatedQuery = ds . applyTemplateVariables ( query , { job : { text : job , value : job } } ) ;
expect ( interpolatedQuery . expr ) . toBe ( job ) ;
2021-10-22 09:58:59 +02:00
} ) ;
2021-11-15 17:11:21 +01:00
it ( 'should add ad-hoc filters to expr' , ( ) = > {
2023-02-09 09:03:13 +00:00
replaceMock . mockImplementation ( ( a : string ) = > a ) ;
getAdhocFiltersMock . mockReturnValue ( [
2021-11-15 17:11:21 +01:00
{
key : 'k1' ,
operator : '=' ,
value : 'v1' ,
} ,
{
key : 'k2' ,
operator : '!=' ,
value : 'v2' ,
} ,
] ) ;
const query = {
expr : 'test{job="bar"}' ,
refId : 'A' ,
} ;
const result = ds . applyTemplateVariables ( query , { } ) ;
2022-03-15 17:37:20 +01:00
expect ( result ) . toMatchObject ( { expr : 'test{job="bar", k1="v1", k2!="v2"}' } ) ;
2021-11-15 17:11:21 +01:00
} ) ;
2021-10-22 09:58:59 +02:00
} ) ;
2021-11-15 17:11:21 +01:00
2019-12-02 18:14:26 +01:00
describe ( 'metricFindQuery' , ( ) = > {
beforeEach ( ( ) = > {
2023-04-03 09:07:17 -05:00
const prometheusDatasource = new PrometheusDatasource (
{ . . . instanceSettings , jsonData : { . . . instanceSettings . jsonData , cacheLevel : PrometheusCacheLevel.None } } ,
templateSrvStub ,
timeSrvStubOld
) ;
2019-12-02 18:14:26 +01:00
const query = 'query_result(topk(5,rate(http_request_duration_microseconds_count[$__interval])))' ;
2023-04-03 09:07:17 -05:00
prometheusDatasource . metricFindQuery ( query ) ;
2019-12-02 18:14:26 +01:00
} ) ;
it ( 'should call templateSrv.replace with scopedVars' , ( ) = > {
2023-02-09 09:03:13 +00:00
expect ( replaceMock . mock . calls [ 0 ] [ 1 ] ) . toBeDefined ( ) ;
2019-12-02 18:14:26 +01:00
} ) ;
it ( 'should have the correct range and range_ms' , ( ) = > {
2023-02-09 09:03:13 +00:00
const range = replaceMock . mock . calls [ 0 ] [ 1 ] . __range ;
const rangeMs = replaceMock . mock . calls [ 0 ] [ 1 ] . __range_ms ;
const rangeS = replaceMock . mock . calls [ 0 ] [ 1 ] . __range_s ;
2019-12-02 18:14:26 +01:00
expect ( range ) . toEqual ( { text : '21s' , value : '21s' } ) ;
expect ( rangeMs ) . toEqual ( { text : 21031 , value : 21031 } ) ;
expect ( rangeS ) . toEqual ( { text : 21 , value : 21 } ) ;
} ) ;
it ( 'should pass the default interval value' , ( ) = > {
2023-02-09 09:03:13 +00:00
const interval = replaceMock . mock . calls [ 0 ] [ 1 ] . __interval ;
const intervalMs = replaceMock . mock . calls [ 0 ] [ 1 ] . __interval_ms ;
2019-12-02 18:14:26 +01:00
expect ( interval ) . toEqual ( { text : '15s' , value : '15s' } ) ;
expect ( intervalMs ) . toEqual ( { text : 15000 , value : 15000 } ) ;
} ) ;
} ) ;
} ) ;
const SECOND = 1000 ;
const MINUTE = 60 * SECOND ;
const HOUR = 60 * MINUTE ;
const time = ( { hours = 0 , seconds = 0 , minutes = 0 } ) = > dateTime ( hours * HOUR + minutes * MINUTE + seconds * SECOND ) ;
2022-08-17 14:37:29 +02:00
describe ( 'PrometheusDatasource2' , ( ) = > {
2022-02-02 12:02:32 +00:00
const instanceSettings = {
2019-12-02 18:14:26 +01:00
url : 'proxied' ,
2022-10-20 07:46:36 -07:00
id : 1 ,
2023-02-06 11:02:02 +01:00
uid : 'ABCDEF' ,
2019-12-02 18:14:26 +01:00
directUrl : 'direct' ,
user : 'test' ,
password : 'mupp' ,
2023-04-03 09:07:17 -05:00
jsonData : { httpMethod : 'GET' , cacheLevel : PrometheusCacheLevel.None } ,
2022-02-02 12:02:32 +00:00
} as unknown as DataSourceInstanceSettings < PromOptions > ;
2019-12-02 18:14:26 +01:00
let ds : PrometheusDatasource ;
beforeEach ( ( ) = > {
2023-02-09 09:03:13 +00:00
ds = new PrometheusDatasource ( instanceSettings , templateSrvStub , timeSrvStub ) ;
2019-12-02 18:14:26 +01:00
} ) ;
describe ( 'When querying prometheus with one target using query editor target spec' , ( ) = > {
describe ( 'and query syntax is valid' , ( ) = > {
2023-02-09 09:03:13 +00:00
let results : DataQueryResponse ;
2019-12-02 18:14:26 +01:00
const query = {
range : { from : time ( { seconds : 63 } ) , to : time ( { seconds : 183 } ) } ,
targets : [ { expr : 'test{job="testjob"}' , format : 'time_series' } ] ,
interval : '60s' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
// Interval alignment with step
const urlExpected = ` proxied/api/v1/query_range?query= ${ encodeURIComponent (
'test{job="testjob"}'
) } & start = 60 & end = 180 & step = 60 ` ;
beforeEach ( async ( ) = > {
const response = {
data : {
status : 'success' ,
data : {
resultType : 'matrix' ,
result : [
{
metric : { __name__ : 'test' , job : 'testjob' } ,
values : [ [ 60 , '3846' ] ] ,
} ,
] ,
} ,
} ,
} ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2023-02-09 09:03:13 +00:00
ds . query ( query ) . subscribe ( ( data ) = > {
2019-12-02 18:14:26 +01:00
results = data ;
} ) ;
} ) ;
it ( 'should generate the correct query' , ( ) = > {
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
} ) ;
it ( 'should return series list' , async ( ) = > {
Field: getFieldTitle as field / series display identity and use it in all field name matchers & field / series name displays (#24024)
* common title handling
* show labels
* update comment
* Update changelog for v7.0.0-beta1 (#24007)
Co-Authored-By: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-Authored-By: Andrej Ocenas <mr.ocenas@gmail.com>
Co-Authored-By: Hugo Häggmark <hugo.haggmark@grafana.com>
Co-authored-by: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com>
* verify-repo-update: Fix Dockerfile.deb (#24030)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* CircleCI: Upgrade build pipeline tool (#24021)
* CircleCI: Upgrade build pipeline tool
* Devenv: ignore enterprise (#24037)
* Add header icon to Add data source page (#24033)
* latest.json: Update testing version (#24038)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* Fix login page redirected from password reset (#24032)
* Storybook: Rewrite stories to CSF (#23989)
* ColorPicker to CSF format
* Convert stories to CSF
* Do not export ClipboardButton
* Update ConfirmButton
* Remove unused imports
* Fix feedback
* changelog enterprise 7.0.0-beta1 (#24039)
* CircleCI: Bump grafana/build-container revision (#24043)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* Changelog: Updates changelog with more feature details (#24040)
* Changelog: Updates changelog with more feature details
* spell fix
* spell fix
* Updates
* Readme update
* Updates
* Select: fixes so component loses focus on selecting value or pressing outside of input. (#24008)
* changed the value container to a class component to get it to work with focus (maybe something with context?).
* added e2e tests to verify that the select focus is working as it should.
* fixed according to feedback.
* updated snapshot.
* Devenv: add remote renderer to grafana (#24050)
* NewPanelEditor: minor UI twekas (#24042)
* Forward ref for tabs, use html props
* Inspect: add inspect label to drawer title
* Add tooltips to sidebar pane tabs, copy changes
* Remove unused import
* Place tooltips over tabs
* Inspector: dont show transformations select if there is only one data frame
* Review
* Changelog: Add a breaking change (#24051)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* CircleCI: Unpin grafana/docs-base (#24054)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* Search: close overlay on Esc press (#24003)
* Search: Close on Esc
* Search: Increase bottom padding for the last item in section
* Search: Move closing search to keybindingsSrv
* Search: Fix folder view
* Search: Do not move folders if already in folder
* Docs: Adds deprecation notice to changelog and docs for scripted dashboards (#24060)
* Update CHANGELOG.md (#24047)
Fix typo
Co-authored-by: Daniel Lee <dan.limerick@gmail.com>
* Documentation: Alternative Team Sync Wording (#23960)
* Alternative wording for team sync docs
Signed-off-by: Joe Elliott <number101010@gmail.com>
* Update docs/sources/auth/team-sync.md
Co-Authored-By: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
* Fix misspell issues (#23905)
* Fix misspell issues
See,
$ golangci-lint run --timeout 10m --disable-all -E misspell ./...
Signed-off-by: Mario Trangoni <mjtrangoni@gmail.com>
* Fix codespell issues
See,
$ codespell -S './.git*' -L 'uint,thru,pres,unknwon,serie,referer,uptodate,durationm'
Signed-off-by: Mario Trangoni <mjtrangoni@gmail.com>
* ci please?
* non-empty commit - ci?
* Trigger build
Co-authored-by: bergquist <carl.bergquist@gmail.com>
Co-authored-by: Kyle Brandt <kyle@grafana.com>
* fix compile error
* better series display
* better display
* now with prometheus and loki
* a few more tests
* Improvements and tests
* thinking
* More advanced and smart default title generation
* Another fix
* Progress but dam this will be hard
* Reverting the time series Value field name change
* revert revert going in circles
* add a field state object
* Use state title when converting back to legacy format
* Improved the join (series to columsn) transformer
* Got tests running again
* Rewrite of seriesToColums that simplifies and fixing tests
* Fixed the tricky problem of multiple time field when not used in join
* Prometheus: Restoring prometheus formatting
* Graphite: Disable Grafana's series naming
* fixed imports
* Fixed tests and made rename transform change title instead
* Fixing more tests
* fix more tests
* fixed import issue
* Fixed more circular dependencies
* Renamed to getFieldTitle
* More rename
* Review feedback
* Fix for showing field title in calculate field transformer
* fieldOverride: Make it clear that state title after applying defaults & overrides
* Fixed ts issue
* Update packages/grafana-ui/src/components/TransformersUI/OrganizeFieldsTransformerEditor.tsx
Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
Co-authored-by: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
Co-authored-by: Leonard Gram <leo@xlson.com>
Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
Co-authored-by: Alexander Zobnin <alexanderzobnin@gmail.com>
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
Co-authored-by: Richard Hartmann <RichiH@users.noreply.github.com>
Co-authored-by: Daniel Lee <dan.limerick@gmail.com>
Co-authored-by: Joe Elliott <joe.elliott@grafana.com>
Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
Co-authored-by: Mario Trangoni <mario@mariotrangoni.de>
Co-authored-by: bergquist <carl.bergquist@gmail.com>
Co-authored-by: Kyle Brandt <kyle@grafana.com>
2020-05-07 01:42:03 -07:00
const frame = toDataFrame ( results . data [ 0 ] ) ;
2019-12-02 18:14:26 +01:00
expect ( results . data . length ) . toBe ( 1 ) ;
2020-08-28 18:22:01 +02:00
expect ( getFieldDisplayName ( frame . fields [ 1 ] , frame ) ) . toBe ( 'test{job="testjob"}' ) ;
2019-12-02 18:14:26 +01:00
} ) ;
} ) ;
describe ( 'and query syntax is invalid' , ( ) = > {
let results : string ;
const query = {
range : { from : time ( { seconds : 63 } ) , to : time ( { seconds : 183 } ) } ,
targets : [ { expr : 'tes;;t{job="testjob"}' , format : 'time_series' } ] ,
interval : '60s' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
const errMessage = 'parse error at char 25: could not parse remaining input' ;
const response = {
data : {
status : 'error' ,
errorType : 'bad_data' ,
error : errMessage ,
} ,
} ;
it ( 'should generate an error' , ( ) = > {
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > throwError ( response ) ) ;
2023-02-09 09:03:13 +00:00
ds . query ( query ) . subscribe ( ( e : any ) = > {
2019-12-02 18:14:26 +01:00
results = e . message ;
expect ( results ) . toBe ( ` " ${ errMessage } " ` ) ;
} ) ;
} ) ;
} ) ;
} ) ;
describe ( 'When querying prometheus with one target which returns multiple series' , ( ) = > {
2023-02-09 09:03:13 +00:00
let results : DataQueryResponse ;
2019-12-02 18:14:26 +01:00
const start = 60 ;
const end = 360 ;
const step = 60 ;
const query = {
range : { from : time ( { seconds : start } ) , to : time ( { seconds : end } ) } ,
targets : [ { expr : 'test{job="testjob"}' , format : 'time_series' } ] ,
interval : '60s' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
beforeEach ( async ( ) = > {
const response = {
status : 'success' ,
data : {
data : {
resultType : 'matrix' ,
result : [
{
metric : { __name__ : 'test' , job : 'testjob' , series : 'series 1' } ,
values : [
[ start + step * 1 , '3846' ] ,
[ start + step * 3 , '3847' ] ,
[ end - step * 1 , '3848' ] ,
] ,
} ,
{
metric : { __name__ : 'test' , job : 'testjob' , series : 'series 2' } ,
values : [ [ start + step * 2 , '4846' ] ] ,
} ,
] ,
} ,
} ,
} ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2019-12-02 18:14:26 +01:00
2023-02-09 09:03:13 +00:00
ds . query ( query ) . subscribe ( ( data ) = > {
2019-12-02 18:14:26 +01:00
results = data ;
} ) ;
} ) ;
it ( 'should be same length' , ( ) = > {
expect ( results . data . length ) . toBe ( 2 ) ;
2020-10-01 12:58:06 +02:00
expect ( results . data [ 0 ] . length ) . toBe ( ( end - start ) / step + 1 ) ;
expect ( results . data [ 1 ] . length ) . toBe ( ( end - start ) / step + 1 ) ;
2019-12-02 18:14:26 +01:00
} ) ;
it ( 'should fill null until first datapoint in response' , ( ) = > {
2020-10-01 12:58:06 +02:00
expect ( results . data [ 0 ] . fields [ 0 ] . values . get ( 0 ) ) . toBe ( start * 1000 ) ;
expect ( results . data [ 0 ] . fields [ 1 ] . values . get ( 0 ) ) . toBe ( null ) ;
expect ( results . data [ 0 ] . fields [ 0 ] . values . get ( 1 ) ) . toBe ( ( start + step * 1 ) * 1000 ) ;
expect ( results . data [ 0 ] . fields [ 1 ] . values . get ( 1 ) ) . toBe ( 3846 ) ;
2019-12-02 18:14:26 +01:00
} ) ;
it ( 'should fill null after last datapoint in response' , ( ) = > {
const length = ( end - start ) / step + 1 ;
2020-10-01 12:58:06 +02:00
expect ( results . data [ 0 ] . fields [ 0 ] . values . get ( length - 2 ) ) . toBe ( ( end - step * 1 ) * 1000 ) ;
expect ( results . data [ 0 ] . fields [ 1 ] . values . get ( length - 2 ) ) . toBe ( 3848 ) ;
expect ( results . data [ 0 ] . fields [ 0 ] . values . get ( length - 1 ) ) . toBe ( end * 1000 ) ;
expect ( results . data [ 0 ] . fields [ 1 ] . values . get ( length - 1 ) ) . toBe ( null ) ;
2019-12-02 18:14:26 +01:00
} ) ;
it ( 'should fill null at gap between series' , ( ) = > {
2020-10-01 12:58:06 +02:00
expect ( results . data [ 0 ] . fields [ 0 ] . values . get ( 2 ) ) . toBe ( ( start + step * 2 ) * 1000 ) ;
expect ( results . data [ 0 ] . fields [ 1 ] . values . get ( 2 ) ) . toBe ( null ) ;
expect ( results . data [ 1 ] . fields [ 0 ] . values . get ( 1 ) ) . toBe ( ( start + step * 1 ) * 1000 ) ;
expect ( results . data [ 1 ] . fields [ 1 ] . values . get ( 1 ) ) . toBe ( null ) ;
expect ( results . data [ 1 ] . fields [ 0 ] . values . get ( 3 ) ) . toBe ( ( start + step * 3 ) * 1000 ) ;
expect ( results . data [ 1 ] . fields [ 1 ] . values . get ( 3 ) ) . toBe ( null ) ;
2019-12-02 18:14:26 +01:00
} ) ;
} ) ;
describe ( 'When querying prometheus with one target and instant = true' , ( ) = > {
2023-02-09 09:03:13 +00:00
let results : DataQueryResponse ;
2023-02-06 11:02:02 +01:00
const urlExpected = ` /api/datasources/uid/ABCDEF/resources/api/v1/query?query= ${ encodeURIComponent (
2022-10-20 07:46:36 -07:00
'test{job="testjob"}'
) } & time = 123 ` ;
2019-12-02 18:14:26 +01:00
const query = {
range : { from : time ( { seconds : 63 } ) , to : time ( { seconds : 123 } ) } ,
targets : [ { expr : 'test{job="testjob"}' , format : 'time_series' , instant : true } ] ,
interval : '60s' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
beforeEach ( async ( ) = > {
const response = {
status : 'success' ,
data : {
data : {
resultType : 'vector' ,
result : [
{
metric : { __name__ : 'test' , job : 'testjob' } ,
value : [ 123 , '3846' ] ,
} ,
] ,
} ,
} ,
} ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2023-02-09 09:03:13 +00:00
ds . query ( query ) . subscribe ( ( data ) = > {
2019-12-02 18:14:26 +01:00
results = data ;
} ) ;
} ) ;
it ( 'should generate the correct query' , ( ) = > {
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
} ) ;
it ( 'should return series list' , ( ) = > {
Field: getFieldTitle as field / series display identity and use it in all field name matchers & field / series name displays (#24024)
* common title handling
* show labels
* update comment
* Update changelog for v7.0.0-beta1 (#24007)
Co-Authored-By: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-Authored-By: Andrej Ocenas <mr.ocenas@gmail.com>
Co-Authored-By: Hugo Häggmark <hugo.haggmark@grafana.com>
Co-authored-by: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com>
* verify-repo-update: Fix Dockerfile.deb (#24030)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* CircleCI: Upgrade build pipeline tool (#24021)
* CircleCI: Upgrade build pipeline tool
* Devenv: ignore enterprise (#24037)
* Add header icon to Add data source page (#24033)
* latest.json: Update testing version (#24038)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* Fix login page redirected from password reset (#24032)
* Storybook: Rewrite stories to CSF (#23989)
* ColorPicker to CSF format
* Convert stories to CSF
* Do not export ClipboardButton
* Update ConfirmButton
* Remove unused imports
* Fix feedback
* changelog enterprise 7.0.0-beta1 (#24039)
* CircleCI: Bump grafana/build-container revision (#24043)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* Changelog: Updates changelog with more feature details (#24040)
* Changelog: Updates changelog with more feature details
* spell fix
* spell fix
* Updates
* Readme update
* Updates
* Select: fixes so component loses focus on selecting value or pressing outside of input. (#24008)
* changed the value container to a class component to get it to work with focus (maybe something with context?).
* added e2e tests to verify that the select focus is working as it should.
* fixed according to feedback.
* updated snapshot.
* Devenv: add remote renderer to grafana (#24050)
* NewPanelEditor: minor UI twekas (#24042)
* Forward ref for tabs, use html props
* Inspect: add inspect label to drawer title
* Add tooltips to sidebar pane tabs, copy changes
* Remove unused import
* Place tooltips over tabs
* Inspector: dont show transformations select if there is only one data frame
* Review
* Changelog: Add a breaking change (#24051)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* CircleCI: Unpin grafana/docs-base (#24054)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* Search: close overlay on Esc press (#24003)
* Search: Close on Esc
* Search: Increase bottom padding for the last item in section
* Search: Move closing search to keybindingsSrv
* Search: Fix folder view
* Search: Do not move folders if already in folder
* Docs: Adds deprecation notice to changelog and docs for scripted dashboards (#24060)
* Update CHANGELOG.md (#24047)
Fix typo
Co-authored-by: Daniel Lee <dan.limerick@gmail.com>
* Documentation: Alternative Team Sync Wording (#23960)
* Alternative wording for team sync docs
Signed-off-by: Joe Elliott <number101010@gmail.com>
* Update docs/sources/auth/team-sync.md
Co-Authored-By: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
* Fix misspell issues (#23905)
* Fix misspell issues
See,
$ golangci-lint run --timeout 10m --disable-all -E misspell ./...
Signed-off-by: Mario Trangoni <mjtrangoni@gmail.com>
* Fix codespell issues
See,
$ codespell -S './.git*' -L 'uint,thru,pres,unknwon,serie,referer,uptodate,durationm'
Signed-off-by: Mario Trangoni <mjtrangoni@gmail.com>
* ci please?
* non-empty commit - ci?
* Trigger build
Co-authored-by: bergquist <carl.bergquist@gmail.com>
Co-authored-by: Kyle Brandt <kyle@grafana.com>
* fix compile error
* better series display
* better display
* now with prometheus and loki
* a few more tests
* Improvements and tests
* thinking
* More advanced and smart default title generation
* Another fix
* Progress but dam this will be hard
* Reverting the time series Value field name change
* revert revert going in circles
* add a field state object
* Use state title when converting back to legacy format
* Improved the join (series to columsn) transformer
* Got tests running again
* Rewrite of seriesToColums that simplifies and fixing tests
* Fixed the tricky problem of multiple time field when not used in join
* Prometheus: Restoring prometheus formatting
* Graphite: Disable Grafana's series naming
* fixed imports
* Fixed tests and made rename transform change title instead
* Fixing more tests
* fix more tests
* fixed import issue
* Fixed more circular dependencies
* Renamed to getFieldTitle
* More rename
* Review feedback
* Fix for showing field title in calculate field transformer
* fieldOverride: Make it clear that state title after applying defaults & overrides
* Fixed ts issue
* Update packages/grafana-ui/src/components/TransformersUI/OrganizeFieldsTransformerEditor.tsx
Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
Co-authored-by: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
Co-authored-by: Leonard Gram <leo@xlson.com>
Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
Co-authored-by: Alexander Zobnin <alexanderzobnin@gmail.com>
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
Co-authored-by: Richard Hartmann <RichiH@users.noreply.github.com>
Co-authored-by: Daniel Lee <dan.limerick@gmail.com>
Co-authored-by: Joe Elliott <joe.elliott@grafana.com>
Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
Co-authored-by: Mario Trangoni <mario@mariotrangoni.de>
Co-authored-by: bergquist <carl.bergquist@gmail.com>
Co-authored-by: Kyle Brandt <kyle@grafana.com>
2020-05-07 01:42:03 -07:00
const frame = toDataFrame ( results . data [ 0 ] ) ;
2019-12-02 18:14:26 +01:00
expect ( results . data . length ) . toBe ( 1 ) ;
Field: getFieldTitle as field / series display identity and use it in all field name matchers & field / series name displays (#24024)
* common title handling
* show labels
* update comment
* Update changelog for v7.0.0-beta1 (#24007)
Co-Authored-By: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-Authored-By: Andrej Ocenas <mr.ocenas@gmail.com>
Co-Authored-By: Hugo Häggmark <hugo.haggmark@grafana.com>
Co-authored-by: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com>
* verify-repo-update: Fix Dockerfile.deb (#24030)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* CircleCI: Upgrade build pipeline tool (#24021)
* CircleCI: Upgrade build pipeline tool
* Devenv: ignore enterprise (#24037)
* Add header icon to Add data source page (#24033)
* latest.json: Update testing version (#24038)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* Fix login page redirected from password reset (#24032)
* Storybook: Rewrite stories to CSF (#23989)
* ColorPicker to CSF format
* Convert stories to CSF
* Do not export ClipboardButton
* Update ConfirmButton
* Remove unused imports
* Fix feedback
* changelog enterprise 7.0.0-beta1 (#24039)
* CircleCI: Bump grafana/build-container revision (#24043)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* Changelog: Updates changelog with more feature details (#24040)
* Changelog: Updates changelog with more feature details
* spell fix
* spell fix
* Updates
* Readme update
* Updates
* Select: fixes so component loses focus on selecting value or pressing outside of input. (#24008)
* changed the value container to a class component to get it to work with focus (maybe something with context?).
* added e2e tests to verify that the select focus is working as it should.
* fixed according to feedback.
* updated snapshot.
* Devenv: add remote renderer to grafana (#24050)
* NewPanelEditor: minor UI twekas (#24042)
* Forward ref for tabs, use html props
* Inspect: add inspect label to drawer title
* Add tooltips to sidebar pane tabs, copy changes
* Remove unused import
* Place tooltips over tabs
* Inspector: dont show transformations select if there is only one data frame
* Review
* Changelog: Add a breaking change (#24051)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* CircleCI: Unpin grafana/docs-base (#24054)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* Search: close overlay on Esc press (#24003)
* Search: Close on Esc
* Search: Increase bottom padding for the last item in section
* Search: Move closing search to keybindingsSrv
* Search: Fix folder view
* Search: Do not move folders if already in folder
* Docs: Adds deprecation notice to changelog and docs for scripted dashboards (#24060)
* Update CHANGELOG.md (#24047)
Fix typo
Co-authored-by: Daniel Lee <dan.limerick@gmail.com>
* Documentation: Alternative Team Sync Wording (#23960)
* Alternative wording for team sync docs
Signed-off-by: Joe Elliott <number101010@gmail.com>
* Update docs/sources/auth/team-sync.md
Co-Authored-By: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
* Fix misspell issues (#23905)
* Fix misspell issues
See,
$ golangci-lint run --timeout 10m --disable-all -E misspell ./...
Signed-off-by: Mario Trangoni <mjtrangoni@gmail.com>
* Fix codespell issues
See,
$ codespell -S './.git*' -L 'uint,thru,pres,unknwon,serie,referer,uptodate,durationm'
Signed-off-by: Mario Trangoni <mjtrangoni@gmail.com>
* ci please?
* non-empty commit - ci?
* Trigger build
Co-authored-by: bergquist <carl.bergquist@gmail.com>
Co-authored-by: Kyle Brandt <kyle@grafana.com>
* fix compile error
* better series display
* better display
* now with prometheus and loki
* a few more tests
* Improvements and tests
* thinking
* More advanced and smart default title generation
* Another fix
* Progress but dam this will be hard
* Reverting the time series Value field name change
* revert revert going in circles
* add a field state object
* Use state title when converting back to legacy format
* Improved the join (series to columsn) transformer
* Got tests running again
* Rewrite of seriesToColums that simplifies and fixing tests
* Fixed the tricky problem of multiple time field when not used in join
* Prometheus: Restoring prometheus formatting
* Graphite: Disable Grafana's series naming
* fixed imports
* Fixed tests and made rename transform change title instead
* Fixing more tests
* fix more tests
* fixed import issue
* Fixed more circular dependencies
* Renamed to getFieldTitle
* More rename
* Review feedback
* Fix for showing field title in calculate field transformer
* fieldOverride: Make it clear that state title after applying defaults & overrides
* Fixed ts issue
* Update packages/grafana-ui/src/components/TransformersUI/OrganizeFieldsTransformerEditor.tsx
Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
Co-authored-by: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
Co-authored-by: Leonard Gram <leo@xlson.com>
Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
Co-authored-by: Alexander Zobnin <alexanderzobnin@gmail.com>
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
Co-authored-by: Richard Hartmann <RichiH@users.noreply.github.com>
Co-authored-by: Daniel Lee <dan.limerick@gmail.com>
Co-authored-by: Joe Elliott <joe.elliott@grafana.com>
Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
Co-authored-by: Mario Trangoni <mario@mariotrangoni.de>
Co-authored-by: bergquist <carl.bergquist@gmail.com>
Co-authored-by: Kyle Brandt <kyle@grafana.com>
2020-05-07 01:42:03 -07:00
expect ( frame . name ) . toBe ( 'test{job="testjob"}' ) ;
2020-08-28 18:22:01 +02:00
expect ( getFieldDisplayName ( frame . fields [ 1 ] , frame ) ) . toBe ( 'test{job="testjob"}' ) ;
2019-12-02 18:14:26 +01:00
} ) ;
} ) ;
2020-02-18 16:10:15 +01:00
describe ( 'annotationQuery' , ( ) = > {
2023-02-09 09:03:13 +00:00
let results : AnnotationEvent [ ] ;
const options = {
2019-12-02 18:14:26 +01:00
annotation : {
expr : 'ALERTS{alertstate="firing"}' ,
tagKeys : 'job' ,
titleFormat : '{{alertname}}' ,
textFormat : '{{instance}}' ,
} ,
range : {
from : time ( { seconds : 63 } ) ,
to : time ( { seconds : 123 } ) ,
} ,
2023-02-09 09:03:13 +00:00
} as unknown as AnnotationQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
2021-11-02 16:46:20 +01:00
const response = createAnnotationResponse ( ) ;
2023-02-22 14:47:50 +01:00
const emptyResponse = createEmptyAnnotationResponse ( ) ;
describe ( 'handle result with empty fields' , ( ) = > {
it ( 'should return empty results' , async ( ) = > {
fetchMock . mockImplementation ( ( ) = > of ( emptyResponse ) ) ;
await ds . annotationQuery ( options ) . then ( ( data ) = > {
results = data ;
} ) ;
expect ( results . length ) . toBe ( 0 ) ;
} ) ;
} ) ;
2019-12-02 18:14:26 +01:00
describe ( 'when time series query is cancelled' , ( ) = > {
it ( 'should return empty results' , async ( ) = > {
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( { cancelled : true } ) ) ;
2019-12-02 18:14:26 +01:00
2023-02-09 09:03:13 +00:00
await ds . annotationQuery ( options ) . then ( ( data ) = > {
2019-12-02 18:14:26 +01:00
results = data ;
} ) ;
expect ( results ) . toEqual ( [ ] ) ;
} ) ;
} ) ;
describe ( 'not use useValueForTime' , ( ) = > {
beforeEach ( async ( ) = > {
options . annotation . useValueForTime = false ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2019-12-02 18:14:26 +01:00
2023-02-09 09:03:13 +00:00
await ds . annotationQuery ( options ) . then ( ( data ) = > {
2019-12-02 18:14:26 +01:00
results = data ;
} ) ;
} ) ;
it ( 'should return annotation list' , ( ) = > {
expect ( results . length ) . toBe ( 1 ) ;
expect ( results [ 0 ] . tags ) . toContain ( 'testjob' ) ;
expect ( results [ 0 ] . title ) . toBe ( 'InstanceDown' ) ;
expect ( results [ 0 ] . text ) . toBe ( 'testinstance' ) ;
2021-11-02 16:46:20 +01:00
expect ( results [ 0 ] . time ) . toBe ( 123 ) ;
2019-12-02 18:14:26 +01:00
} ) ;
} ) ;
describe ( 'use useValueForTime' , ( ) = > {
beforeEach ( async ( ) = > {
options . annotation . useValueForTime = true ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2019-12-02 18:14:26 +01:00
2023-02-09 09:03:13 +00:00
await ds . annotationQuery ( options ) . then ( ( data ) = > {
2019-12-02 18:14:26 +01:00
results = data ;
} ) ;
} ) ;
it ( 'should return annotation list' , ( ) = > {
2021-11-02 16:46:20 +01:00
expect ( results [ 0 ] . time ) . toEqual ( 456 ) ;
2019-12-02 18:14:26 +01:00
} ) ;
} ) ;
describe ( 'step parameter' , ( ) = > {
beforeEach ( ( ) = > {
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2019-12-02 18:14:26 +01:00
} ) ;
it ( 'should use default step for short range if no interval is given' , ( ) = > {
const query = {
. . . options ,
range : {
from : time ( { seconds : 63 } ) ,
to : time ( { seconds : 123 } ) ,
} ,
2023-02-09 09:03:13 +00:00
} as AnnotationQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
ds . annotationQuery ( query ) ;
2020-08-20 21:03:59 -07:00
const req = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2021-11-02 16:46:20 +01:00
expect ( req . data . queries [ 0 ] . interval ) . toBe ( '60s' ) ;
2019-12-02 18:14:26 +01:00
} ) ;
2020-02-05 16:40:37 +01:00
it ( 'should use default step for short range when annotation step is empty string' , ( ) = > {
const query = {
. . . options ,
annotation : {
. . . options . annotation ,
step : '' ,
} ,
range : {
from : time ( { seconds : 63 } ) ,
to : time ( { seconds : 123 } ) ,
} ,
2023-02-09 09:03:13 +00:00
} as unknown as AnnotationQueryRequest < PromQuery > ;
2020-02-05 16:40:37 +01:00
ds . annotationQuery ( query ) ;
2020-08-20 21:03:59 -07:00
const req = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2021-11-02 16:46:20 +01:00
expect ( req . data . queries [ 0 ] . interval ) . toBe ( '60s' ) ;
2019-12-02 18:14:26 +01:00
} ) ;
it ( 'should use custom step for short range' , ( ) = > {
const annotation = {
. . . options . annotation ,
step : '10s' ,
} ;
const query = {
. . . options ,
annotation ,
range : {
from : time ( { seconds : 63 } ) ,
to : time ( { seconds : 123 } ) ,
} ,
2023-02-09 09:03:13 +00:00
} as unknown as AnnotationQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
ds . annotationQuery ( query ) ;
2020-08-20 21:03:59 -07:00
const req = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2021-11-02 16:46:20 +01:00
expect ( req . data . queries [ 0 ] . interval ) . toBe ( '10s' ) ;
2019-12-02 18:14:26 +01:00
} ) ;
} ) ;
2020-02-18 16:10:15 +01:00
describe ( 'region annotations for sectors' , ( ) = > {
const options : any = {
annotation : {
expr : 'ALERTS{alertstate="firing"}' ,
tagKeys : 'job' ,
titleFormat : '{{alertname}}' ,
textFormat : '{{instance}}' ,
} ,
range : {
from : time ( { seconds : 63 } ) ,
to : time ( { seconds : 900 } ) ,
} ,
} ;
2021-11-02 16:46:20 +01:00
async function runAnnotationQuery ( data : number [ ] [ ] ) {
let response = createAnnotationResponse ( ) ;
response . data . results [ 'X' ] . frames [ 0 ] . data . values = data ;
2020-02-18 16:10:15 +01:00
options . annotation . useValueForTime = false ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2020-02-18 16:10:15 +01:00
return ds . annotationQuery ( options ) ;
}
it ( 'should handle gaps and inactive values' , async ( ) = > {
const results = await runAnnotationQuery ( [
2021-11-02 16:46:20 +01:00
[ 2 * 60000 , 3 * 60000 , 5 * 60000 , 6 * 60000 , 7 * 60000 , 8 * 60000 , 9 * 60000 ] ,
[ 1 , 1 , 1 , 1 , 1 , 0 , 1 ] ,
2020-02-18 16:10:15 +01:00
] ) ;
2021-01-20 07:59:48 +01:00
expect ( results . map ( ( result ) = > [ result . time , result . timeEnd ] ) ) . toEqual ( [
2020-02-18 16:10:15 +01:00
[ 120000 , 180000 ] ,
[ 300000 , 420000 ] ,
[ 540000 , 540000 ] ,
] ) ;
} ) ;
it ( 'should handle single region' , async ( ) = > {
const results = await runAnnotationQuery ( [
2021-11-02 16:46:20 +01:00
[ 2 * 60000 , 3 * 60000 ] ,
[ 1 , 1 ] ,
2020-02-18 16:10:15 +01:00
] ) ;
2021-01-20 07:59:48 +01:00
expect ( results . map ( ( result ) = > [ result . time , result . timeEnd ] ) ) . toEqual ( [ [ 120000 , 180000 ] ] ) ;
2020-02-18 16:10:15 +01:00
} ) ;
it ( 'should handle 0 active regions' , async ( ) = > {
const results = await runAnnotationQuery ( [
2021-11-02 16:46:20 +01:00
[ 2 * 60000 , 3 * 60000 , 5 * 60000 ] ,
[ 0 , 0 , 0 ] ,
2020-02-18 16:10:15 +01:00
] ) ;
expect ( results . length ) . toBe ( 0 ) ;
} ) ;
it ( 'should handle single active value' , async ( ) = > {
2021-11-02 16:46:20 +01:00
const results = await runAnnotationQuery ( [ [ 2 * 60000 ] , [ 1 ] ] ) ;
2021-01-20 07:59:48 +01:00
expect ( results . map ( ( result ) = > [ result . time , result . timeEnd ] ) ) . toEqual ( [ [ 120000 , 120000 ] ] ) ;
2020-02-18 16:10:15 +01:00
} ) ;
} ) ;
2023-02-09 09:03:13 +00:00
2021-12-22 16:50:58 +01:00
describe ( 'with template variables' , ( ) = > {
afterAll ( ( ) = > {
2023-02-09 09:03:13 +00:00
replaceMock . mockImplementation ( ( a : string , . . . rest : unknown [ ] ) = > a ) ;
2021-12-22 16:50:58 +01:00
} ) ;
it ( 'should interpolate variables in query expr' , ( ) = > {
const query = {
. . . options ,
annotation : {
. . . options . annotation ,
expr : '$variable' ,
} ,
range : {
from : time ( { seconds : 1 } ) ,
to : time ( { seconds : 2 } ) ,
} ,
2023-02-09 09:03:13 +00:00
} as unknown as AnnotationQueryRequest < PromQuery > ;
2021-12-22 16:50:58 +01:00
const interpolated = 'interpolated_expr' ;
2023-02-09 09:03:13 +00:00
replaceMock . mockReturnValue ( interpolated ) ;
2021-12-22 16:50:58 +01:00
ds . annotationQuery ( query ) ;
const req = fetchMock . mock . calls [ 0 ] [ 0 ] ;
expect ( req . data . queries [ 0 ] . expr ) . toBe ( interpolated ) ;
} ) ;
} ) ;
2019-12-02 18:14:26 +01:00
} ) ;
describe ( 'When resultFormat is table and instant = true' , ( ) = > {
2023-02-09 09:03:13 +00:00
let results : DataQueryResponse ;
2019-12-02 18:14:26 +01:00
const query = {
range : { from : time ( { seconds : 63 } ) , to : time ( { seconds : 123 } ) } ,
targets : [ { expr : 'test{job="testjob"}' , format : 'time_series' , instant : true } ] ,
interval : '60s' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
beforeEach ( async ( ) = > {
const response = {
status : 'success' ,
data : {
data : {
resultType : 'vector' ,
result : [
{
metric : { __name__ : 'test' , job : 'testjob' } ,
value : [ 123 , '3846' ] ,
} ,
] ,
} ,
} ,
} ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2023-02-09 09:03:13 +00:00
ds . query ( query ) . subscribe ( ( data : any ) = > {
2019-12-02 18:14:26 +01:00
results = data ;
} ) ;
} ) ;
it ( 'should return result' , ( ) = > {
expect ( results ) . not . toBe ( null ) ;
} ) ;
} ) ;
describe ( 'The "step" query parameter' , ( ) = > {
const response = {
status : 'success' ,
data : {
data : {
resultType : 'matrix' ,
result : [ ] as DataQueryResponseData [ ] ,
} ,
} ,
} ;
it ( 'should be min interval when greater than auto interval' , async ( ) = > {
const query = {
// 6 minute range
range : { from : time ( { minutes : 1 } ) , to : time ( { minutes : 7 } ) } ,
targets : [
{
expr : 'test' ,
interval : '10s' ,
} ,
] ,
interval : '5s' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
const urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=10' ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2023-02-09 09:03:13 +00:00
ds . query ( query ) ;
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
} ) ;
2020-02-06 09:28:20 -05:00
it ( 'step should be fractional for sub second intervals' , async ( ) = > {
2019-12-02 18:14:26 +01:00
const query = {
// 6 minute range
range : { from : time ( { minutes : 1 } ) , to : time ( { minutes : 7 } ) } ,
targets : [ { expr : 'test' } ] ,
interval : '100ms' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2020-02-06 09:28:20 -05:00
const urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=0.1' ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2023-02-09 09:03:13 +00:00
ds . query ( query ) ;
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
} ) ;
it ( 'should be auto interval when greater than min interval' , async ( ) = > {
const query = {
// 6 minute range
range : { from : time ( { minutes : 1 } ) , to : time ( { minutes : 7 } ) } ,
targets : [
{
expr : 'test' ,
interval : '5s' ,
} ,
] ,
interval : '10s' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
const urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=10' ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2023-02-09 09:03:13 +00:00
ds . query ( query ) ;
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
} ) ;
it ( 'should result in querying fewer than 11000 data points' , async ( ) = > {
const query = {
// 6 hour range
range : { from : time ( { hours : 1 } ) , to : time ( { hours : 7 } ) } ,
targets : [ { expr : 'test' } ] ,
interval : '1s' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
const end = 7 * 60 * 60 ;
const start = 60 * 60 ;
const urlExpected = 'proxied/api/v1/query_range?query=test&start=' + start + '&end=' + end + '&step=2' ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2023-02-09 09:03:13 +00:00
ds . query ( query ) ;
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
} ) ;
it ( 'should not apply min interval when interval * intervalFactor greater' , async ( ) = > {
const query = {
// 6 minute range
range : { from : time ( { minutes : 1 } ) , to : time ( { minutes : 7 } ) } ,
targets : [
{
expr : 'test' ,
interval : '10s' ,
intervalFactor : 10 ,
} ,
] ,
interval : '5s' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
// times get rounded up to interval
const urlExpected = 'proxied/api/v1/query_range?query=test&start=50&end=400&step=50' ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2023-02-09 09:03:13 +00:00
ds . query ( query ) ;
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
} ) ;
it ( 'should apply min interval when interval * intervalFactor smaller' , async ( ) = > {
const query = {
// 6 minute range
range : { from : time ( { minutes : 1 } ) , to : time ( { minutes : 7 } ) } ,
targets : [
{
expr : 'test' ,
interval : '15s' ,
intervalFactor : 2 ,
} ,
] ,
interval : '5s' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
const urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=60&end=420&step=15' ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2023-02-09 09:03:13 +00:00
ds . query ( query ) ;
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
} ) ;
it ( 'should apply intervalFactor to auto interval when greater' , async ( ) = > {
const query = {
// 6 minute range
range : { from : time ( { minutes : 1 } ) , to : time ( { minutes : 7 } ) } ,
targets : [
{
expr : 'test' ,
interval : '5s' ,
intervalFactor : 10 ,
} ,
] ,
interval : '10s' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
// times get aligned to interval
const urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=0&end=400&step=100' ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2023-02-09 09:03:13 +00:00
ds . query ( query ) ;
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
} ) ;
it ( 'should not not be affected by the 11000 data points limit when large enough' , async ( ) = > {
const query = {
// 1 week range
range : { from : time ( { } ) , to : time ( { hours : 7 * 24 } ) } ,
targets : [
{
expr : 'test' ,
intervalFactor : 10 ,
} ,
] ,
interval : '10s' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
const end = 7 * 24 * 60 * 60 ;
const start = 0 ;
const urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=' + start + '&end=' + end + '&step=100' ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2023-02-09 09:03:13 +00:00
ds . query ( query ) ;
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
} ) ;
it ( 'should be determined by the 11000 data points limit when too small' , async ( ) = > {
const query = {
// 1 week range
range : { from : time ( { } ) , to : time ( { hours : 7 * 24 } ) } ,
targets : [
{
expr : 'test' ,
intervalFactor : 10 ,
} ,
] ,
interval : '5s' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2019-12-23 08:28:08 +01:00
let end = 7 * 24 * 60 * 60 ;
end -= end % 55 ;
2019-12-02 18:14:26 +01:00
const start = 0 ;
2020-01-07 15:15:33 +01:00
const step = 55 ;
2020-10-01 18:51:23 +01:00
const adjusted = alignRange ( start , end , step , timeSrvStub . timeRange ( ) . to . utcOffset ( ) * 60 ) ;
2020-01-07 15:15:33 +01:00
const urlExpected =
2023-04-03 09:07:17 -05:00
'proxied/api/v1/query_range?query=test' +
'&start=' +
adjusted . start +
'&end=' +
( adjusted . end + step ) +
'&step=' +
step ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2023-02-09 09:03:13 +00:00
ds . query ( query ) ;
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
} ) ;
} ) ;
describe ( 'The __interval and __interval_ms template variables' , ( ) = > {
const response = {
status : 'success' ,
data : {
data : {
resultType : 'matrix' ,
result : [ ] as DataQueryResponseData [ ] ,
} ,
} ,
} ;
it ( 'should be unchanged when auto interval is greater than min interval' , async ( ) = > {
const query = {
// 6 minute range
range : { from : time ( { minutes : 1 } ) , to : time ( { minutes : 7 } ) } ,
targets : [
{
expr : 'rate(test[$__interval])' ,
interval : '5s' ,
} ,
] ,
interval : '10s' ,
scopedVars : {
__interval : { text : '10s' , value : '10s' } ,
__interval_ms : { text : 10 * 1000 , value : 10 * 1000 } ,
} ,
} ;
const urlExpected =
'proxied/api/v1/query_range?query=' +
encodeURIComponent ( 'rate(test[$__interval])' ) +
'&start=60&end=420&step=10' ;
2023-02-09 09:03:13 +00:00
replaceMock . mockImplementation ( ( str ) = > str ) ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2019-12-02 18:14:26 +01:00
ds . query ( query as any ) ;
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
2023-02-09 09:03:13 +00:00
expect ( replaceMock . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
2019-12-02 18:14:26 +01:00
__interval : {
text : '10s' ,
value : '10s' ,
} ,
__interval_ms : {
text : 10000 ,
value : 10000 ,
} ,
} ) ;
} ) ;
it ( 'should be min interval when it is greater than auto interval' , async ( ) = > {
const query = {
// 6 minute range
range : { from : time ( { minutes : 1 } ) , to : time ( { minutes : 7 } ) } ,
targets : [
{
expr : 'rate(test[$__interval])' ,
interval : '10s' ,
} ,
] ,
interval : '5s' ,
scopedVars : {
__interval : { text : '5s' , value : '5s' } ,
__interval_ms : { text : 5 * 1000 , value : 5 * 1000 } ,
} ,
} ;
const urlExpected =
'proxied/api/v1/query_range?query=' +
encodeURIComponent ( 'rate(test[$__interval])' ) +
'&start=60&end=420&step=10' ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2023-02-09 09:03:13 +00:00
replaceMock . mockImplementation ( ( str ) = > str ) ;
2019-12-02 18:14:26 +01:00
ds . query ( query as any ) ;
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
2023-02-09 09:03:13 +00:00
expect ( replaceMock . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
2019-12-02 18:14:26 +01:00
__interval : {
text : '5s' ,
value : '5s' ,
} ,
__interval_ms : {
text : 5000 ,
value : 5000 ,
} ,
} ) ;
} ) ;
it ( 'should account for intervalFactor' , async ( ) = > {
const query = {
// 6 minute range
range : { from : time ( { minutes : 1 } ) , to : time ( { minutes : 7 } ) } ,
targets : [
{
expr : 'rate(test[$__interval])' ,
interval : '5s' ,
intervalFactor : 10 ,
} ,
] ,
interval : '10s' ,
scopedVars : {
__interval : { text : '10s' , value : '10s' } ,
__interval_ms : { text : 10 * 1000 , value : 10 * 1000 } ,
} ,
} ;
const urlExpected =
'proxied/api/v1/query_range?query=' +
encodeURIComponent ( 'rate(test[$__interval])' ) +
'&start=0&end=400&step=100' ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2023-02-09 09:03:13 +00:00
replaceMock . mockImplementation ( ( str ) = > str ) ;
2019-12-02 18:14:26 +01:00
ds . query ( query as any ) ;
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
2023-02-09 09:03:13 +00:00
expect ( replaceMock . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
2019-12-02 18:14:26 +01:00
__interval : {
text : '10s' ,
value : '10s' ,
} ,
__interval_ms : {
text : 10000 ,
value : 10000 ,
} ,
} ) ;
expect ( query . scopedVars . __interval . text ) . toBe ( '10s' ) ;
expect ( query . scopedVars . __interval . value ) . toBe ( '10s' ) ;
expect ( query . scopedVars . __interval_ms . text ) . toBe ( 10 * 1000 ) ;
expect ( query . scopedVars . __interval_ms . value ) . toBe ( 10 * 1000 ) ;
} ) ;
it ( 'should be interval * intervalFactor when greater than min interval' , async ( ) = > {
const query = {
// 6 minute range
range : { from : time ( { minutes : 1 } ) , to : time ( { minutes : 7 } ) } ,
targets : [
{
expr : 'rate(test[$__interval])' ,
interval : '10s' ,
intervalFactor : 10 ,
} ,
] ,
interval : '5s' ,
scopedVars : {
__interval : { text : '5s' , value : '5s' } ,
__interval_ms : { text : 5 * 1000 , value : 5 * 1000 } ,
} ,
} ;
const urlExpected =
'proxied/api/v1/query_range?query=' +
encodeURIComponent ( 'rate(test[$__interval])' ) +
'&start=50&end=400&step=50' ;
2023-02-09 09:03:13 +00:00
replaceMock . mockImplementation ( ( str ) = > str ) ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2019-12-02 18:14:26 +01:00
ds . query ( query as any ) ;
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
2023-02-09 09:03:13 +00:00
expect ( replaceMock . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
2019-12-02 18:14:26 +01:00
__interval : {
text : '5s' ,
value : '5s' ,
} ,
__interval_ms : {
text : 5000 ,
value : 5000 ,
} ,
} ) ;
} ) ;
it ( 'should be min interval when greater than interval * intervalFactor' , async ( ) = > {
const query = {
// 6 minute range
range : { from : time ( { minutes : 1 } ) , to : time ( { minutes : 7 } ) } ,
targets : [
{
expr : 'rate(test[$__interval])' ,
interval : '15s' ,
intervalFactor : 2 ,
} ,
] ,
interval : '5s' ,
scopedVars : {
__interval : { text : '5s' , value : '5s' } ,
__interval_ms : { text : 5 * 1000 , value : 5 * 1000 } ,
} ,
} ;
const urlExpected =
'proxied/api/v1/query_range?query=' +
encodeURIComponent ( 'rate(test[$__interval])' ) +
'&start=60&end=420&step=15' ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2019-12-02 18:14:26 +01:00
ds . query ( query as any ) ;
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
2023-02-09 09:03:13 +00:00
expect ( replaceMock . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
2019-12-02 18:14:26 +01:00
__interval : {
text : '5s' ,
value : '5s' ,
} ,
__interval_ms : {
text : 5000 ,
value : 5000 ,
} ,
} ) ;
} ) ;
it ( 'should be determined by the 11000 data points limit, accounting for intervalFactor' , async ( ) = > {
const query = {
// 1 week range
range : { from : time ( { } ) , to : time ( { hours : 7 * 24 } ) } ,
targets : [
{
expr : 'rate(test[$__interval])' ,
intervalFactor : 10 ,
} ,
] ,
interval : '5s' ,
scopedVars : {
__interval : { text : '5s' , value : '5s' } ,
__interval_ms : { text : 5 * 1000 , value : 5 * 1000 } ,
} ,
} ;
2019-12-23 08:28:08 +01:00
let end = 7 * 24 * 60 * 60 ;
end -= end % 55 ;
2019-12-02 18:14:26 +01:00
const start = 0 ;
2020-01-07 15:15:33 +01:00
const step = 55 ;
2020-10-01 18:51:23 +01:00
const adjusted = alignRange ( start , end , step , timeSrvStub . timeRange ( ) . to . utcOffset ( ) * 60 ) ;
2019-12-02 18:14:26 +01:00
const urlExpected =
'proxied/api/v1/query_range?query=' +
encodeURIComponent ( 'rate(test[$__interval])' ) +
'&start=' +
2020-01-07 15:15:33 +01:00
adjusted . start +
2019-12-02 18:14:26 +01:00
'&end=' +
2023-04-03 09:07:17 -05:00
( adjusted . end + step ) +
2020-01-07 15:15:33 +01:00
'&step=' +
step ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2023-02-09 09:03:13 +00:00
replaceMock . mockImplementation ( ( str ) = > str ) ;
2019-12-02 18:14:26 +01:00
ds . query ( query as any ) ;
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'GET' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
2023-02-09 09:03:13 +00:00
expect ( replaceMock . mock . calls [ 0 ] [ 1 ] ) . toEqual ( {
2019-12-02 18:14:26 +01:00
__interval : {
text : '5s' ,
value : '5s' ,
} ,
__interval_ms : {
text : 5000 ,
value : 5000 ,
} ,
} ) ;
} ) ;
} ) ;
describe ( 'The __range, __range_s and __range_ms variables' , ( ) = > {
const response = {
status : 'success' ,
data : {
data : {
resultType : 'matrix' ,
result : [ ] as DataQueryResponseData [ ] ,
} ,
} ,
} ;
it ( 'should use overridden ranges, not dashboard ranges' , async ( ) = > {
const expectedRangeSecond = 3600 ;
2020-02-11 15:28:06 +02:00
const expectedRangeString = '3600s' ;
2019-12-02 18:14:26 +01:00
const query = {
range : {
from : time ( { } ) ,
to : time ( { hours : 1 } ) ,
} ,
targets : [
{
expr : 'test[${__range_s}s]' ,
} ,
] ,
interval : '60s' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
const urlExpected = ` proxied/api/v1/query_range?query= ${ encodeURIComponent (
query . targets [ 0 ] . expr
) } & start = 0 & end = 3600 & step = 60 ` ;
2023-02-09 09:03:13 +00:00
replaceMock . mockImplementation ( ( str ) = > str ) ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2023-02-09 09:03:13 +00:00
ds . query ( query ) ;
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . url ) . toBe ( urlExpected ) ;
2023-02-09 09:03:13 +00:00
expect ( replaceMock . mock . calls [ 1 ] [ 1 ] ) . toEqual ( {
2019-12-02 18:14:26 +01:00
__range_s : {
text : expectedRangeSecond ,
value : expectedRangeSecond ,
} ,
__range : {
text : expectedRangeString ,
value : expectedRangeString ,
} ,
__range_ms : {
text : expectedRangeSecond * 1000 ,
value : expectedRangeSecond * 1000 ,
} ,
2020-08-13 18:58:40 +02:00
__rate_interval : {
text : '75s' ,
value : '75s' ,
} ,
2019-12-02 18:14:26 +01:00
} ) ;
} ) ;
} ) ;
2020-08-13 18:58:40 +02:00
describe ( 'The __rate_interval variable' , ( ) = > {
2020-08-25 18:55:08 +02:00
const target = { expr : 'rate(process_cpu_seconds_total[$__rate_interval])' , refId : 'A' } ;
beforeEach ( ( ) = > {
2023-02-09 09:03:13 +00:00
replaceMock . mockClear ( ) ;
2020-08-25 18:55:08 +02:00
} ) ;
2020-08-13 18:58:40 +02:00
it ( 'should be 4 times the scrape interval if interval + scrape interval is lower' , ( ) = > {
2023-02-09 09:03:13 +00:00
ds . createQuery ( target , { interval : '15s' } as DataQueryRequest < PromQuery > , 0 , 300 ) ;
expect ( replaceMock . mock . calls [ 1 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '60s' ) ;
2020-08-13 18:58:40 +02:00
} ) ;
it ( 'should be interval + scrape interval if 4 times the scrape interval is lower' , ( ) = > {
2023-02-09 09:03:13 +00:00
ds . createQuery ( target , { interval : '5m' } as DataQueryRequest < PromQuery > , 0 , 10080 ) ;
expect ( replaceMock . mock . calls [ 1 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '315s' ) ;
2020-08-25 18:55:08 +02:00
} ) ;
it ( 'should fall back to a scrape interval of 15s if min step is set to 0, resulting in 4*15s = 60s' , ( ) = > {
2023-02-09 09:03:13 +00:00
ds . createQuery ( { . . . target , interval : '' } , { interval : '15s' } as DataQueryRequest < PromQuery > , 0 , 300 ) ;
expect ( replaceMock . mock . calls [ 1 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '60s' ) ;
2020-08-25 18:55:08 +02:00
} ) ;
it ( 'should be 4 times the scrape interval if min step set to 1m and interval is 15s' , ( ) = > {
// For a 5m graph, $__interval is 15s
2023-02-09 09:03:13 +00:00
ds . createQuery ( { . . . target , interval : '1m' } , { interval : '15s' } as DataQueryRequest < PromQuery > , 0 , 300 ) ;
expect ( replaceMock . mock . calls [ 2 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '240s' ) ;
2020-08-25 18:55:08 +02:00
} ) ;
it ( 'should be interval + scrape interval if min step set to 1m and interval is 5m' , ( ) = > {
// For a 7d graph, $__interval is 5m
2023-02-09 09:03:13 +00:00
ds . createQuery ( { . . . target , interval : '1m' } , { interval : '5m' } as DataQueryRequest < PromQuery > , 0 , 10080 ) ;
expect ( replaceMock . mock . calls [ 2 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '360s' ) ;
2020-08-25 18:55:08 +02:00
} ) ;
it ( 'should be interval + scrape interval if resolution is set to 1/2 and interval is 10m' , ( ) = > {
// For a 7d graph, $__interval is 10m
2023-02-09 09:03:13 +00:00
ds . createQuery ( { . . . target , intervalFactor : 2 } , { interval : '10m' } as DataQueryRequest < PromQuery > , 0 , 10080 ) ;
expect ( replaceMock . mock . calls [ 1 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '1215s' ) ;
2020-08-25 18:55:08 +02:00
} ) ;
it ( 'should be 4 times the scrape interval if resolution is set to 1/2 and interval is 15s' , ( ) = > {
// For a 5m graph, $__interval is 15s
2023-02-09 09:03:13 +00:00
ds . createQuery ( { . . . target , intervalFactor : 2 } , { interval : '15s' } as DataQueryRequest < PromQuery > , 0 , 300 ) ;
expect ( replaceMock . mock . calls [ 1 ] [ 1 ] [ '__rate_interval' ] . value ) . toBe ( '60s' ) ;
2020-08-13 18:58:40 +02:00
} ) ;
2020-09-10 20:31:53 +02:00
it ( 'should interpolate min step if set' , ( ) = > {
2023-02-09 09:03:13 +00:00
replaceMock . mockImplementation ( ( _ : string ) = > '15s' ) ;
ds . createQuery ( { . . . target , interval : '$int' } , { interval : '15s' } as DataQueryRequest < PromQuery > , 0 , 300 ) ;
expect ( replaceMock . mock . calls ) . toHaveLength ( 3 ) ;
replaceMock . mockImplementation ( ( str ) = > str ) ;
2020-09-10 20:31:53 +02:00
} ) ;
2020-08-13 18:58:40 +02:00
} ) ;
2021-12-09 11:30:23 +01:00
it ( 'should give back 1 exemplar target when multiple queries with exemplar enabled and same metric' , ( ) = > {
const targetA : PromQuery = {
refId : 'A' ,
expr : 'histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[5m])) by (le))' ,
exemplar : true ,
} ;
const targetB : PromQuery = {
refId : 'B' ,
expr : 'histogram_quantile(0.5, sum(rate(tns_request_duration_seconds_bucket[5m])) by (le))' ,
exemplar : true ,
} ;
ds . languageProvider = {
histogramMetrics : [ 'tns_request_duration_seconds_bucket' ] ,
2023-02-09 09:03:13 +00:00
} as PromQlLanguageProvider ;
2021-12-09 11:30:23 +01:00
2022-02-02 12:02:32 +00:00
const request = {
2021-12-09 11:30:23 +01:00
targets : [ targetA , targetB ] ,
interval : '1s' ,
panelId : '' ,
2022-10-25 10:04:35 +01:00
} as unknown as DataQueryRequest < PromQuery > ;
2021-12-09 11:30:23 +01:00
const Aexemplars = ds . shouldRunExemplarQuery ( targetA , request ) ;
const BExpemplars = ds . shouldRunExemplarQuery ( targetB , request ) ;
expect ( Aexemplars ) . toBe ( true ) ;
expect ( BExpemplars ) . toBe ( false ) ;
} ) ;
2019-12-02 18:14:26 +01:00
} ) ;
describe ( 'PrometheusDatasource for POST' , ( ) = > {
2022-02-02 12:02:32 +00:00
const instanceSettings = {
2019-12-02 18:14:26 +01:00
url : 'proxied' ,
directUrl : 'direct' ,
user : 'test' ,
password : 'mupp' ,
jsonData : { httpMethod : 'POST' } ,
2022-02-02 12:02:32 +00:00
} as unknown as DataSourceInstanceSettings < PromOptions > ;
2019-12-02 18:14:26 +01:00
let ds : PrometheusDatasource ;
beforeEach ( ( ) = > {
2023-02-09 09:03:13 +00:00
ds = new PrometheusDatasource ( instanceSettings , templateSrvStub , timeSrvStub ) ;
2019-12-02 18:14:26 +01:00
} ) ;
describe ( 'When querying prometheus with one target using query editor target spec' , ( ) = > {
2023-02-09 09:03:13 +00:00
let results : DataQueryResponse ;
2019-12-02 18:14:26 +01:00
const urlExpected = 'proxied/api/v1/query_range' ;
const dataExpected = {
query : 'test{job="testjob"}' ,
start : 1 * 60 ,
end : 2 * 60 ,
step : 60 ,
} ;
const query = {
range : { from : time ( { minutes : 1 , seconds : 3 } ) , to : time ( { minutes : 2 , seconds : 3 } ) } ,
targets : [ { expr : 'test{job="testjob"}' , format : 'time_series' } ] ,
interval : '60s' ,
2023-02-09 09:03:13 +00:00
} as DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
beforeEach ( async ( ) = > {
const response = {
status : 'success' ,
data : {
data : {
resultType : 'matrix' ,
result : [
{
metric : { __name__ : 'test' , job : 'testjob' } ,
values : [ [ 2 * 60 , '3846' ] ] ,
} ,
] ,
} ,
} ,
} ;
2020-08-20 21:03:59 -07:00
fetchMock . mockImplementation ( ( ) = > of ( response ) ) ;
2023-02-09 09:03:13 +00:00
ds . query ( query ) . subscribe ( ( data ) = > {
2019-12-02 18:14:26 +01:00
results = data ;
} ) ;
} ) ;
it ( 'should generate the correct query' , ( ) = > {
2020-08-20 21:03:59 -07:00
const res = fetchMock . mock . calls [ 0 ] [ 0 ] ;
2019-12-02 18:14:26 +01:00
expect ( res . method ) . toBe ( 'POST' ) ;
expect ( res . url ) . toBe ( urlExpected ) ;
expect ( res . data ) . toEqual ( dataExpected ) ;
} ) ;
it ( 'should return series list' , ( ) = > {
Field: getFieldTitle as field / series display identity and use it in all field name matchers & field / series name displays (#24024)
* common title handling
* show labels
* update comment
* Update changelog for v7.0.0-beta1 (#24007)
Co-Authored-By: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-Authored-By: Andrej Ocenas <mr.ocenas@gmail.com>
Co-Authored-By: Hugo Häggmark <hugo.haggmark@grafana.com>
Co-authored-by: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com>
* verify-repo-update: Fix Dockerfile.deb (#24030)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* CircleCI: Upgrade build pipeline tool (#24021)
* CircleCI: Upgrade build pipeline tool
* Devenv: ignore enterprise (#24037)
* Add header icon to Add data source page (#24033)
* latest.json: Update testing version (#24038)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* Fix login page redirected from password reset (#24032)
* Storybook: Rewrite stories to CSF (#23989)
* ColorPicker to CSF format
* Convert stories to CSF
* Do not export ClipboardButton
* Update ConfirmButton
* Remove unused imports
* Fix feedback
* changelog enterprise 7.0.0-beta1 (#24039)
* CircleCI: Bump grafana/build-container revision (#24043)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* Changelog: Updates changelog with more feature details (#24040)
* Changelog: Updates changelog with more feature details
* spell fix
* spell fix
* Updates
* Readme update
* Updates
* Select: fixes so component loses focus on selecting value or pressing outside of input. (#24008)
* changed the value container to a class component to get it to work with focus (maybe something with context?).
* added e2e tests to verify that the select focus is working as it should.
* fixed according to feedback.
* updated snapshot.
* Devenv: add remote renderer to grafana (#24050)
* NewPanelEditor: minor UI twekas (#24042)
* Forward ref for tabs, use html props
* Inspect: add inspect label to drawer title
* Add tooltips to sidebar pane tabs, copy changes
* Remove unused import
* Place tooltips over tabs
* Inspector: dont show transformations select if there is only one data frame
* Review
* Changelog: Add a breaking change (#24051)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* CircleCI: Unpin grafana/docs-base (#24054)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
* Search: close overlay on Esc press (#24003)
* Search: Close on Esc
* Search: Increase bottom padding for the last item in section
* Search: Move closing search to keybindingsSrv
* Search: Fix folder view
* Search: Do not move folders if already in folder
* Docs: Adds deprecation notice to changelog and docs for scripted dashboards (#24060)
* Update CHANGELOG.md (#24047)
Fix typo
Co-authored-by: Daniel Lee <dan.limerick@gmail.com>
* Documentation: Alternative Team Sync Wording (#23960)
* Alternative wording for team sync docs
Signed-off-by: Joe Elliott <number101010@gmail.com>
* Update docs/sources/auth/team-sync.md
Co-Authored-By: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
* Fix misspell issues (#23905)
* Fix misspell issues
See,
$ golangci-lint run --timeout 10m --disable-all -E misspell ./...
Signed-off-by: Mario Trangoni <mjtrangoni@gmail.com>
* Fix codespell issues
See,
$ codespell -S './.git*' -L 'uint,thru,pres,unknwon,serie,referer,uptodate,durationm'
Signed-off-by: Mario Trangoni <mjtrangoni@gmail.com>
* ci please?
* non-empty commit - ci?
* Trigger build
Co-authored-by: bergquist <carl.bergquist@gmail.com>
Co-authored-by: Kyle Brandt <kyle@grafana.com>
* fix compile error
* better series display
* better display
* now with prometheus and loki
* a few more tests
* Improvements and tests
* thinking
* More advanced and smart default title generation
* Another fix
* Progress but dam this will be hard
* Reverting the time series Value field name change
* revert revert going in circles
* add a field state object
* Use state title when converting back to legacy format
* Improved the join (series to columsn) transformer
* Got tests running again
* Rewrite of seriesToColums that simplifies and fixing tests
* Fixed the tricky problem of multiple time field when not used in join
* Prometheus: Restoring prometheus formatting
* Graphite: Disable Grafana's series naming
* fixed imports
* Fixed tests and made rename transform change title instead
* Fixing more tests
* fix more tests
* fixed import issue
* Fixed more circular dependencies
* Renamed to getFieldTitle
* More rename
* Review feedback
* Fix for showing field title in calculate field transformer
* fieldOverride: Make it clear that state title after applying defaults & overrides
* Fixed ts issue
* Update packages/grafana-ui/src/components/TransformersUI/OrganizeFieldsTransformerEditor.tsx
Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
Co-authored-by: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
Co-authored-by: Leonard Gram <leo@xlson.com>
Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
Co-authored-by: Alexander Zobnin <alexanderzobnin@gmail.com>
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
Co-authored-by: Richard Hartmann <RichiH@users.noreply.github.com>
Co-authored-by: Daniel Lee <dan.limerick@gmail.com>
Co-authored-by: Joe Elliott <joe.elliott@grafana.com>
Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
Co-authored-by: Mario Trangoni <mario@mariotrangoni.de>
Co-authored-by: bergquist <carl.bergquist@gmail.com>
Co-authored-by: Kyle Brandt <kyle@grafana.com>
2020-05-07 01:42:03 -07:00
const frame = toDataFrame ( results . data [ 0 ] ) ;
2019-12-02 18:14:26 +01:00
expect ( results . data . length ) . toBe ( 1 ) ;
2020-08-28 18:22:01 +02:00
expect ( getFieldDisplayName ( frame . fields [ 1 ] , frame ) ) . toBe ( 'test{job="testjob"}' ) ;
2019-12-02 18:14:26 +01:00
} ) ;
} ) ;
2022-08-10 10:36:19 +02:00
describe ( 'When querying prometheus via check headers X-Dashboard-Id X-Panel-Id and X-Dashboard-UID' , ( ) = > {
2023-02-09 09:03:13 +00:00
const options = { dashboardId : 1 , panelId : 2 , dashboardUID : 'WFlOM-jM1' } as DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
const httpOptions = {
headers : { } as { [ key : string ] : number | undefined } ,
2023-02-09 09:03:13 +00:00
} as PromQueryRequest ;
2022-09-28 20:05:52 +03:00
const instanceSettings = {
url : 'proxied' ,
directUrl : 'direct' ,
user : 'test' ,
password : 'mupp' ,
access : 'proxy' ,
jsonData : { httpMethod : 'POST' } ,
} as unknown as DataSourceInstanceSettings < PromOptions > ;
let ds : PrometheusDatasource ;
beforeEach ( ( ) = > {
ds = new PrometheusDatasource (
instanceSettings ,
templateSrvStub as unknown as TemplateSrv ,
timeSrvStub as unknown as TimeSrv
) ;
} ) ;
2019-12-02 18:14:26 +01:00
it ( 'with proxy access tracing headers should be added' , ( ) = > {
2023-02-09 09:03:13 +00:00
ds . _addTracingHeaders ( httpOptions , options ) ;
2022-08-10 10:36:19 +02:00
expect ( httpOptions . headers [ 'X-Dashboard-Id' ] ) . toBe ( options . dashboardId ) ;
expect ( httpOptions . headers [ 'X-Panel-Id' ] ) . toBe ( options . panelId ) ;
expect ( httpOptions . headers [ 'X-Dashboard-UID' ] ) . toBe ( options . dashboardUID ) ;
2019-12-02 18:14:26 +01:00
} ) ;
it ( 'with direct access tracing headers should not be added' , ( ) = > {
2022-09-28 20:05:52 +03:00
const instanceSettings = {
url : 'proxied' ,
directUrl : 'direct' ,
user : 'test' ,
password : 'mupp' ,
jsonData : { httpMethod : 'POST' } ,
} as unknown as DataSourceInstanceSettings < PromOptions > ;
2020-10-01 18:51:23 +01:00
const mockDs = new PrometheusDatasource (
{ . . . instanceSettings , url : 'http://127.0.0.1:8000' } ,
2023-02-09 09:03:13 +00:00
templateSrvStub ,
timeSrvStub
2020-10-01 18:51:23 +01:00
) ;
2023-02-09 09:03:13 +00:00
mockDs . _addTracingHeaders ( httpOptions , options ) ;
2019-12-02 18:14:26 +01:00
expect ( httpOptions . headers [ 'X-Dashboard-Id' ] ) . toBe ( undefined ) ;
expect ( httpOptions . headers [ 'X-Panel-Id' ] ) . toBe ( undefined ) ;
2022-08-10 10:36:19 +02:00
expect ( httpOptions . headers [ 'X-Dashboard-UID' ] ) . toBe ( undefined ) ;
2019-12-02 18:14:26 +01:00
} ) ;
} ) ;
} ) ;
2021-05-28 15:40:15 +02:00
function getPrepareTargetsContext ( {
targets ,
app ,
queryOptions ,
languageProvider ,
} : {
targets : PromQuery [ ] ;
app? : CoreApp ;
queryOptions? : Partial < QueryOptions > ;
2023-02-09 09:03:13 +00:00
languageProvider? : PromQlLanguageProvider ;
2021-05-28 15:40:15 +02:00
} ) {
2022-02-02 12:02:32 +00:00
const instanceSettings = {
2019-12-02 18:14:26 +01:00
url : 'proxied' ,
directUrl : 'direct' ,
2022-09-28 20:05:52 +03:00
access : 'proxy' ,
2019-12-02 18:14:26 +01:00
user : 'test' ,
password : 'mupp' ,
jsonData : { httpMethod : 'POST' } ,
2022-02-02 12:02:32 +00:00
} as unknown as DataSourceInstanceSettings < PromOptions > ;
2019-12-02 18:14:26 +01:00
const start = 0 ;
const end = 1 ;
const panelId = '2' ;
2022-02-02 12:02:32 +00:00
const options = {
2021-05-28 15:40:15 +02:00
targets ,
2021-01-20 07:59:48 +01:00
interval : '1s' ,
panelId ,
app ,
. . . queryOptions ,
2022-10-25 10:04:35 +01:00
} as unknown as DataQueryRequest < PromQuery > ;
2019-12-02 18:14:26 +01:00
2023-02-09 09:03:13 +00:00
const ds = new PrometheusDatasource ( instanceSettings , templateSrvStub , timeSrvStub ) ;
2021-05-28 15:40:15 +02:00
if ( languageProvider ) {
ds . languageProvider = languageProvider ;
}
2019-12-02 18:14:26 +01:00
const { queries , activeTargets } = ds . prepareTargets ( options , start , end ) ;
return {
queries ,
activeTargets ,
start ,
end ,
panelId ,
} ;
2021-05-28 15:40:15 +02:00
}
2019-12-02 18:14:26 +01:00
describe ( 'prepareTargets' , ( ) = > {
describe ( 'when run from a Panel' , ( ) = > {
it ( 'then it should just add targets' , ( ) = > {
const target : PromQuery = {
refId : 'A' ,
expr : 'up' ,
} ;
2021-05-28 15:40:15 +02:00
const { queries , activeTargets , panelId , end , start } = getPrepareTargetsContext ( { targets : [ target ] } ) ;
2019-12-02 18:14:26 +01:00
expect ( queries . length ) . toBe ( 1 ) ;
expect ( activeTargets . length ) . toBe ( 1 ) ;
expect ( queries [ 0 ] ) . toEqual ( {
end ,
expr : 'up' ,
headers : {
'X-Dashboard-Id' : undefined ,
2022-08-10 10:36:19 +02:00
'X-Dashboard-UID' : undefined ,
2019-12-02 18:14:26 +01:00
'X-Panel-Id' : panelId ,
} ,
hinting : undefined ,
instant : undefined ,
refId : target.refId ,
start ,
step : 1 ,
} ) ;
expect ( activeTargets [ 0 ] ) . toEqual ( target ) ;
} ) ;
2021-05-28 15:40:15 +02:00
it ( 'should give back 3 targets when multiple queries with exemplar enabled and same metric' , ( ) = > {
const targetA : PromQuery = {
refId : 'A' ,
expr : 'histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[5m])) by (le))' ,
exemplar : true ,
} ;
const targetB : PromQuery = {
refId : 'B' ,
expr : 'histogram_quantile(0.5, sum(rate(tns_request_duration_seconds_bucket[5m])) by (le))' ,
exemplar : true ,
} ;
const { queries , activeTargets } = getPrepareTargetsContext ( {
targets : [ targetA , targetB ] ,
languageProvider : {
histogramMetrics : [ 'tns_request_duration_seconds_bucket' ] ,
2023-02-09 09:03:13 +00:00
} as PromQlLanguageProvider ,
2021-05-28 15:40:15 +02:00
} ) ;
expect ( queries ) . toHaveLength ( 3 ) ;
expect ( activeTargets ) . toHaveLength ( 3 ) ;
} ) ;
it ( 'should give back 4 targets when multiple queries with exemplar enabled' , ( ) = > {
const targetA : PromQuery = {
refId : 'A' ,
expr : 'histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[5m])) by (le))' ,
exemplar : true ,
} ;
const targetB : PromQuery = {
refId : 'B' ,
expr : 'histogram_quantile(0.5, sum(rate(tns_request_duration_bucket[5m])) by (le))' ,
exemplar : true ,
} ;
const { queries , activeTargets } = getPrepareTargetsContext ( {
targets : [ targetA , targetB ] ,
languageProvider : {
histogramMetrics : [ 'tns_request_duration_seconds_bucket' ] ,
2023-02-09 09:03:13 +00:00
} as PromQlLanguageProvider ,
2021-05-28 15:40:15 +02:00
} ) ;
expect ( queries ) . toHaveLength ( 4 ) ;
expect ( activeTargets ) . toHaveLength ( 4 ) ;
} ) ;
2021-04-06 18:35:00 +02:00
it ( 'should give back 2 targets when exemplar enabled' , ( ) = > {
const target : PromQuery = {
refId : 'A' ,
expr : 'up' ,
exemplar : true ,
} ;
2021-05-28 15:40:15 +02:00
const { queries , activeTargets } = getPrepareTargetsContext ( { targets : [ target ] } ) ;
2021-04-06 18:35:00 +02:00
expect ( queries ) . toHaveLength ( 2 ) ;
expect ( activeTargets ) . toHaveLength ( 2 ) ;
expect ( activeTargets [ 0 ] . exemplar ) . toBe ( true ) ;
expect ( activeTargets [ 1 ] . exemplar ) . toBe ( false ) ;
} ) ;
it ( 'should give back 1 target when exemplar and instant are enabled' , ( ) = > {
const target : PromQuery = {
refId : 'A' ,
expr : 'up' ,
exemplar : true ,
instant : true ,
} ;
2021-05-28 15:40:15 +02:00
const { queries , activeTargets } = getPrepareTargetsContext ( { targets : [ target ] } ) ;
2021-04-06 18:35:00 +02:00
expect ( queries ) . toHaveLength ( 1 ) ;
expect ( activeTargets ) . toHaveLength ( 1 ) ;
expect ( activeTargets [ 0 ] . instant ) . toBe ( true ) ;
} ) ;
2019-12-02 18:14:26 +01:00
} ) ;
describe ( 'when run from Explore' , ( ) = > {
2020-09-22 17:31:42 +02:00
describe ( 'when query type Both is selected' , ( ) = > {
2021-05-28 15:40:15 +02:00
it ( 'should give back 6 targets when multiple queries with exemplar enabled' , ( ) = > {
const targetA : PromQuery = {
refId : 'A' ,
expr : 'histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[5m])) by (le))' ,
instant : true ,
range : true ,
exemplar : true ,
} ;
const targetB : PromQuery = {
refId : 'B' ,
expr : 'histogram_quantile(0.5, sum(rate(tns_request_duration_bucket[5m])) by (le))' ,
exemplar : true ,
instant : true ,
range : true ,
} ;
const { queries , activeTargets } = getPrepareTargetsContext ( {
targets : [ targetA , targetB ] ,
app : CoreApp.Explore ,
languageProvider : {
histogramMetrics : [ 'tns_request_duration_seconds_bucket' ] ,
2023-02-09 09:03:13 +00:00
} as PromQlLanguageProvider ,
2021-05-28 15:40:15 +02:00
} ) ;
expect ( queries ) . toHaveLength ( 6 ) ;
expect ( activeTargets ) . toHaveLength ( 6 ) ;
} ) ;
it ( 'should give back 5 targets when multiple queries with exemplar enabled and same metric' , ( ) = > {
const targetA : PromQuery = {
refId : 'A' ,
expr : 'histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[5m])) by (le))' ,
instant : true ,
range : true ,
exemplar : true ,
} ;
const targetB : PromQuery = {
refId : 'B' ,
expr : 'histogram_quantile(0.5, sum(rate(tns_request_duration_seconds_bucket[5m])) by (le))' ,
exemplar : true ,
instant : true ,
range : true ,
} ;
const { queries , activeTargets } = getPrepareTargetsContext ( {
targets : [ targetA , targetB ] ,
app : CoreApp.Explore ,
languageProvider : {
histogramMetrics : [ 'tns_request_duration_seconds_bucket' ] ,
2023-02-09 09:03:13 +00:00
} as PromQlLanguageProvider ,
2021-05-28 15:40:15 +02:00
} ) ;
expect ( queries ) . toHaveLength ( 5 ) ;
expect ( activeTargets ) . toHaveLength ( 5 ) ;
} ) ;
2019-12-02 18:14:26 +01:00
it ( 'then it should return both instant and time series related objects' , ( ) = > {
const target : PromQuery = {
refId : 'A' ,
expr : 'up' ,
2020-09-22 17:31:42 +02:00
range : true ,
instant : true ,
2019-12-02 18:14:26 +01:00
} ;
2021-05-28 15:40:15 +02:00
const { queries , activeTargets , panelId , end , start } = getPrepareTargetsContext ( {
targets : [ target ] ,
app : CoreApp.Explore ,
} ) ;
2019-12-02 18:14:26 +01:00
expect ( queries . length ) . toBe ( 2 ) ;
expect ( activeTargets . length ) . toBe ( 2 ) ;
expect ( queries [ 0 ] ) . toEqual ( {
end ,
expr : 'up' ,
headers : {
'X-Dashboard-Id' : undefined ,
2022-08-10 10:36:19 +02:00
'X-Dashboard-UID' : undefined ,
2019-12-02 18:14:26 +01:00
'X-Panel-Id' : panelId ,
} ,
hinting : undefined ,
instant : true ,
refId : target.refId ,
start ,
step : 1 ,
} ) ;
expect ( activeTargets [ 0 ] ) . toEqual ( {
. . . target ,
format : 'table' ,
instant : true ,
valueWithRefId : true ,
} ) ;
expect ( queries [ 1 ] ) . toEqual ( {
end ,
expr : 'up' ,
headers : {
'X-Dashboard-Id' : undefined ,
2022-08-10 10:36:19 +02:00
'X-Dashboard-UID' : undefined ,
2019-12-02 18:14:26 +01:00
'X-Panel-Id' : panelId ,
} ,
hinting : undefined ,
instant : false ,
refId : target.refId ,
start ,
step : 1 ,
} ) ;
expect ( activeTargets [ 1 ] ) . toEqual ( {
. . . target ,
format : 'time_series' ,
instant : false ,
} ) ;
} ) ;
} ) ;
2020-09-22 17:31:42 +02:00
describe ( 'when query type Instant is selected' , ( ) = > {
2020-09-25 14:34:33 +02:00
it ( 'then it should target and modify its format to table' , ( ) = > {
2019-12-02 18:14:26 +01:00
const target : PromQuery = {
refId : 'A' ,
expr : 'up' ,
2020-09-22 17:31:42 +02:00
instant : true ,
range : false ,
2019-12-02 18:14:26 +01:00
} ;
2021-05-28 15:40:15 +02:00
const { queries , activeTargets , panelId , end , start } = getPrepareTargetsContext ( {
targets : [ target ] ,
app : CoreApp.Explore ,
} ) ;
2019-12-02 18:14:26 +01:00
expect ( queries . length ) . toBe ( 1 ) ;
expect ( activeTargets . length ) . toBe ( 1 ) ;
expect ( queries [ 0 ] ) . toEqual ( {
end ,
expr : 'up' ,
headers : {
'X-Dashboard-Id' : undefined ,
2022-08-10 10:36:19 +02:00
'X-Dashboard-UID' : undefined ,
2019-12-02 18:14:26 +01:00
'X-Panel-Id' : panelId ,
} ,
hinting : undefined ,
instant : true ,
refId : target.refId ,
start ,
step : 1 ,
} ) ;
2020-09-25 14:34:33 +02:00
expect ( activeTargets [ 0 ] ) . toEqual ( { . . . target , format : 'table' } ) ;
2019-12-02 18:14:26 +01:00
} ) ;
} ) ;
2020-09-22 17:31:42 +02:00
} ) ;
2019-12-02 18:14:26 +01:00
2020-09-22 17:31:42 +02:00
describe ( 'when query type Range is selected' , ( ) = > {
it ( 'then it should just add targets' , ( ) = > {
const target : PromQuery = {
refId : 'A' ,
expr : 'up' ,
range : true ,
instant : false ,
} ;
2019-12-02 18:14:26 +01:00
2021-05-28 15:40:15 +02:00
const { queries , activeTargets , panelId , end , start } = getPrepareTargetsContext ( {
targets : [ target ] ,
app : CoreApp.Explore ,
} ) ;
2019-12-02 18:14:26 +01:00
2020-09-22 17:31:42 +02:00
expect ( queries . length ) . toBe ( 1 ) ;
expect ( activeTargets . length ) . toBe ( 1 ) ;
expect ( queries [ 0 ] ) . toEqual ( {
end ,
expr : 'up' ,
headers : {
'X-Dashboard-Id' : undefined ,
2022-08-10 10:36:19 +02:00
'X-Dashboard-UID' : undefined ,
2020-09-22 17:31:42 +02:00
'X-Panel-Id' : panelId ,
} ,
hinting : undefined ,
instant : false ,
refId : target.refId ,
start ,
step : 1 ,
2019-12-02 18:14:26 +01:00
} ) ;
2020-09-22 17:31:42 +02:00
expect ( activeTargets [ 0 ] ) . toEqual ( target ) ;
2019-12-02 18:14:26 +01:00
} ) ;
} ) ;
2019-09-13 15:08:29 +02:00
} ) ;
2020-06-10 07:09:02 +02:00
describe ( 'modifyQuery' , ( ) = > {
describe ( 'when called with ADD_FILTER' , ( ) = > {
describe ( 'and query has no labels' , ( ) = > {
it ( 'then the correct label should be added' , ( ) = > {
const query : PromQuery = { refId : 'A' , expr : 'go_goroutines' } ;
2022-07-18 14:13:34 +02:00
const action = { options : { key : 'cluster' , value : 'us-cluster' } , type : 'ADD_FILTER' } ;
2022-02-02 12:02:32 +00:00
const instanceSettings = { jsonData : { } } as unknown as DataSourceInstanceSettings < PromOptions > ;
2023-02-09 09:03:13 +00:00
const ds = new PrometheusDatasource ( instanceSettings , templateSrvStub , timeSrvStub ) ;
2020-06-10 07:09:02 +02:00
const result = ds . modifyQuery ( query , action ) ;
expect ( result . refId ) . toEqual ( 'A' ) ;
expect ( result . expr ) . toEqual ( 'go_goroutines{cluster="us-cluster"}' ) ;
} ) ;
} ) ;
describe ( 'and query has labels' , ( ) = > {
it ( 'then the correct label should be added' , ( ) = > {
const query : PromQuery = { refId : 'A' , expr : 'go_goroutines{cluster="us-cluster"}' } ;
2022-07-18 14:13:34 +02:00
const action = { options : { key : 'pod' , value : 'pod-123' } , type : 'ADD_FILTER' } ;
2022-02-02 12:02:32 +00:00
const instanceSettings = { jsonData : { } } as unknown as DataSourceInstanceSettings < PromOptions > ;
2023-02-09 09:03:13 +00:00
const ds = new PrometheusDatasource ( instanceSettings , templateSrvStub , timeSrvStub ) ;
2020-06-10 07:09:02 +02:00
const result = ds . modifyQuery ( query , action ) ;
expect ( result . refId ) . toEqual ( 'A' ) ;
2022-03-15 17:37:20 +01:00
expect ( result . expr ) . toEqual ( 'go_goroutines{cluster="us-cluster", pod="pod-123"}' ) ;
2020-06-10 07:09:02 +02:00
} ) ;
} ) ;
} ) ;
describe ( 'when called with ADD_FILTER_OUT' , ( ) = > {
describe ( 'and query has no labels' , ( ) = > {
it ( 'then the correct label should be added' , ( ) = > {
const query : PromQuery = { refId : 'A' , expr : 'go_goroutines' } ;
2022-07-18 14:13:34 +02:00
const action = { options : { key : 'cluster' , value : 'us-cluster' } , type : 'ADD_FILTER_OUT' } ;
2022-02-02 12:02:32 +00:00
const instanceSettings = { jsonData : { } } as unknown as DataSourceInstanceSettings < PromOptions > ;
2023-02-09 09:03:13 +00:00
const ds = new PrometheusDatasource ( instanceSettings , templateSrvStub , timeSrvStub ) ;
2020-06-10 07:09:02 +02:00
const result = ds . modifyQuery ( query , action ) ;
expect ( result . refId ) . toEqual ( 'A' ) ;
expect ( result . expr ) . toEqual ( 'go_goroutines{cluster!="us-cluster"}' ) ;
} ) ;
} ) ;
describe ( 'and query has labels' , ( ) = > {
it ( 'then the correct label should be added' , ( ) = > {
const query : PromQuery = { refId : 'A' , expr : 'go_goroutines{cluster="us-cluster"}' } ;
2022-07-18 14:13:34 +02:00
const action = { options : { key : 'pod' , value : 'pod-123' } , type : 'ADD_FILTER_OUT' } ;
2022-02-02 12:02:32 +00:00
const instanceSettings = { jsonData : { } } as unknown as DataSourceInstanceSettings < PromOptions > ;
2023-02-09 09:03:13 +00:00
const ds = new PrometheusDatasource ( instanceSettings , templateSrvStub , timeSrvStub ) ;
2020-06-10 07:09:02 +02:00
const result = ds . modifyQuery ( query , action ) ;
expect ( result . refId ) . toEqual ( 'A' ) ;
2022-03-15 17:37:20 +01:00
expect ( result . expr ) . toEqual ( 'go_goroutines{cluster="us-cluster", pod!="pod-123"}' ) ;
2020-06-10 07:09:02 +02:00
} ) ;
} ) ;
} ) ;
} ) ;
2019-12-27 09:11:16 +01:00
function createDataRequest ( targets : any [ ] , overrides? : Partial < DataQueryRequest > ) : DataQueryRequest < PromQuery > {
const defaults = {
app : CoreApp.Dashboard ,
2021-01-20 07:59:48 +01:00
targets : targets.map ( ( t ) = > {
2019-09-13 15:08:29 +02:00
return {
instant : false ,
start : dateTime ( ) . subtract ( 5 , 'minutes' ) ,
end : dateTime ( ) ,
expr : 'test' ,
. . . t ,
} ;
} ) ,
range : {
from : dateTime ( ) ,
to : dateTime ( ) ,
} ,
interval : '15s' ,
2020-07-16 14:00:28 +01:00
showingGraph : true ,
2019-09-13 15:08:29 +02:00
} ;
2019-12-27 09:11:16 +01:00
return Object . assign ( defaults , overrides || { } ) as DataQueryRequest < PromQuery > ;
2019-09-13 15:08:29 +02:00
}
2020-01-21 09:08:07 +00:00
function createDefaultPromResponse() {
return {
data : {
data : {
result : [
{
metric : {
__name__ : 'test_metric' ,
} ,
values : [ [ 1568369640 , 1 ] ] ,
} ,
] ,
resultType : 'matrix' ,
} ,
} ,
} ;
}
2021-11-02 16:46:20 +01:00
function createAnnotationResponse() {
const response = {
data : {
results : {
X : {
frames : [
{
schema : {
name : 'bar' ,
refId : 'X' ,
fields : [
{
name : 'Time' ,
type : 'time' ,
typeInfo : {
frame : 'time.Time' ,
} ,
} ,
{
name : 'Value' ,
type : 'number' ,
typeInfo : {
frame : 'float64' ,
} ,
labels : {
__name__ : 'ALERTS' ,
alertname : 'InstanceDown' ,
alertstate : 'firing' ,
instance : 'testinstance' ,
job : 'testjob' ,
} ,
} ,
] ,
} ,
data : {
values : [ [ 123 ] , [ 456 ] ] ,
} ,
} ,
] ,
} ,
} ,
} ,
} ;
return { . . . response } ;
}
2023-02-22 14:47:50 +01:00
function createEmptyAnnotationResponse() {
const response = {
data : {
results : {
X : {
frames : [
{
schema : {
name : 'bar' ,
refId : 'X' ,
fields : [ ] ,
} ,
data : {
values : [ ] ,
} ,
} ,
] ,
} ,
} ,
} ,
} ;
return { . . . response } ;
}