@ -111,11 +111,11 @@ describe('Graphite query model', () => {
} ) ;
} ) ;
describe ( 'when query has multiple seriesByTags functions as parameters it updates the model target correctly ' , ( ) = > {
describe ( 'When the second parameter of a function is a function, the graphite parser breaks ' , ( ) = > {
/ *
all functions that take parameters as functions can have a bug where writing a query
in code with two seriesByTags funcs as params and then
switching from code to builder parsers the second function in a way that
in code where the second parameter of the function IS A FUNCTION ,
then switching from code to builder parsers the second function in a way that
changes the order of the params and wraps the first param in the second param .
asPercent ( seriesByTag ( 'namespace=asd' ) , ( seriesByTag ( 'namespace=fgh' ) )
@ -126,33 +126,123 @@ describe('Graphite query model', () => {
where each function is wrapped in another function
https : //github.com/grafana/grafana/blob/main/public/app/plugins/datasource/graphite/graphite_query.ts#LL187C8-L187C8
Parsing the second seriesByTag function as param as a string fixes this issue
Parsing the second "function as param" as a string fixes this issue
This is one of the edge cases that could be a reason for either refactoring or rebuilding the Graphite query builder
* /
beforeEach ( ( ) = > {
ctx . target = { refId : 'A' , target : ` asPercent(seriesByTag('namespace=asd'), seriesByTag('namespace=fgh')) ` } ;
ctx . targets = [ ctx . target ] ;
ctx . queryModel = new GraphiteQuery ( ctx . datasource , ctx . target , ctx . templateSrv ) ;
describe ( 'when query has multiple seriesByTags functions as parameters it updates the model target correctly' , ( ) = > {
beforeEach ( ( ) = > {
ctx . target = { refId : 'A' , target : ` asPercent(seriesByTag('namespace=asd'), seriesByTag('namespace=fgh')) ` } ;
ctx . targets = [ ctx . target ] ;
ctx . queryModel = new GraphiteQuery ( ctx . datasource , ctx . target , ctx . templateSrv ) ;
} ) ;
it ( 'should parse the second function param as a string and not a second function' , ( ) = > {
const targets = [
{
refId : 'A' ,
datasource : {
type : 'graphite' ,
uid : 'zzz' ,
} ,
target : "asPercent(seriesByTag('namespace=jkl'), seriesByTag('namespace=fgh'))" ,
textEditor : false ,
key : '123' ,
} ,
] ;
expect ( ctx . queryModel . segments . length ) . toBe ( 0 ) ;
expect ( ctx . queryModel . functions . length ) . toBe ( 2 ) ;
ctx . queryModel . updateModelTarget ( targets ) ;
expect ( ctx . queryModel . target . target ) . not . toContain ( 'seriesByTag(seriesByTag(' ) ;
} ) ;
} ) ;
it ( 'should parse the second function param as a string and not a second function' , ( ) = > {
const targets = [
{
describe ( 'when query has divideSeriesLists function where second parameter is a function is parses correctly' , ( ) = > {
it ( 'should parse the second function param as a string and not parse it as a second function' , ( ) = > {
const functionAsParam = 'scaleToSeconds(carbon.agents.0df7e0ba2701-a.cache.queries,1)' ;
ctx . target = {
refId : 'A' ,
datasource : {
type : 'graphite' ,
uid : 'zzz' ,
target : ` divideSeriesLists(scaleToSeconds(nonNegativeDerivative(carbon.agents.0df7e0ba2701-a.cache.queries), 1), ${ functionAsParam } ) ` ,
} ;
ctx . targets = [ ctx . target ] ;
ctx . queryModel = new GraphiteQuery ( ctx . datasource , ctx . target , ctx . templateSrv ) ;
const targets = [
{
refId : 'A' ,
datasource : {
type : 'graphite' ,
uid : 'zzz' ,
} ,
target : ` divideSeriesLists(scaleToSeconds(nonNegativeDerivative(carbon.agents.0df7e0ba2701-a.cache.queries), 1), ${ functionAsParam } ) ` ,
textEditor : false ,
key : '123' ,
} ,
target : "asPercent(seriesByTag('namespace=jkl'), seriesByTag('namespace=fgh'))" ,
textEditor : false ,
key : '123' ,
} ,
] ;
expect ( ctx . queryModel . segments . length ) . toBe ( 0 ) ;
expect ( ctx . queryModel . functions . length ) . toBe ( 2 ) ;
ctx . queryModel . updateModelTarget ( targets ) ;
expect ( ctx . queryModel . target . target ) . not . toContain ( 'seriesByTag(seriesByTag(' ) ;
] ;
expect ( ctx . queryModel . segments . length ) . toBe ( 5 ) ;
expect ( ctx . queryModel . functions . length ) . toBe ( 3 ) ;
ctx . queryModel . updateModelTarget ( targets ) ;
expect ( ctx . queryModel . target . target ) . toContain ( functionAsParam ) ;
} ) ;
it ( 'should recursively parse a second function argument that contains another function as a string' , ( ) = > {
const nestedFunctionAsParam =
'scaleToSeconds(nonNegativeDerivative(carbon.agents.0df7e0ba2701-a.cache.queries,1))' ;
ctx . target = {
refId : 'A' ,
target : ` divideSeriesLists(scaleToSeconds(nonNegativeDerivative(carbon.agents.0df7e0ba2701-a.cache.queries), 1), ${ nestedFunctionAsParam } ) ` ,
} ;
ctx . targets = [ ctx . target ] ;
ctx . queryModel = new GraphiteQuery ( ctx . datasource , ctx . target , ctx . templateSrv ) ;
const targets = [
{
refId : 'A' ,
datasource : {
type : 'graphite' ,
uid : 'zzz' ,
} ,
target : ` divideSeriesLists(scaleToSeconds(nonNegativeDerivative(carbon.agents.0df7e0ba2701-a.cache.queries), 1), ${ nestedFunctionAsParam } ) ` ,
textEditor : false ,
key : '123' ,
} ,
] ;
expect ( ctx . queryModel . segments . length ) . toBe ( 5 ) ;
expect ( ctx . queryModel . functions . length ) . toBe ( 3 ) ;
ctx . queryModel . updateModelTarget ( targets ) ;
expect ( ctx . queryModel . target . target ) . toContain ( nestedFunctionAsParam ) ;
} ) ;
it ( 'should recursively parse a second function argument where the first argument is a series' , ( ) = > {
const nestedFunctionAsParam =
'scaleToSeconds(nonNegativeDerivative(carbon.agents.0df7e0ba2701-a.cache.queries,1))' ;
ctx . target = {
refId : 'A' ,
target : ` divideSeriesLists(carbon.agents.0df7e0ba2701-a.cache.queries, ${ nestedFunctionAsParam } ) ` ,
} ;
ctx . targets = [ ctx . target ] ;
ctx . queryModel = new GraphiteQuery ( ctx . datasource , ctx . target , ctx . templateSrv ) ;
const targets = [
{
refId : 'A' ,
datasource : {
type : 'graphite' ,
uid : 'zzz' ,
} ,
target : ` divideSeriesLists(scaleToSeconds(nonNegativeDerivative(carbon.agents.0df7e0ba2701-a.cache.queries), 1), ${ nestedFunctionAsParam } ) ` ,
textEditor : false ,
key : '123' ,
} ,
] ;
expect ( ctx . queryModel . segments . length ) . toBe ( 5 ) ;
expect ( ctx . queryModel . functions . length ) . toBe ( 1 ) ;
ctx . queryModel . updateModelTarget ( targets ) ;
expect ( ctx . queryModel . target . target ) . toContain ( nestedFunctionAsParam ) ;
} ) ;
} ) ;
} ) ;
} ) ;