diff --git a/public/app/features/explore/utils/links.test.ts b/public/app/features/explore/utils/links.test.ts index 23c2f7be0b3..1c60871ed87 100644 --- a/public/app/features/explore/utils/links.test.ts +++ b/public/app/features/explore/utils/links.test.ts @@ -133,7 +133,7 @@ describe('getFieldLinksForExplore', () => { expect(links).toHaveLength(0); }); - it('returns internal links when target contains defined template variables', () => { + it('returns internal links when target contains __data template variables', () => { const { field, range, dataFrame } = setup({ title: '', url: '', @@ -145,6 +145,100 @@ describe('getFieldLinksForExplore', () => { }); const links = getFieldLinksForExplore({ field, rowIndex: ROW_WITH_TEXT_VALUE.index, range, dataFrame }); expect(links).toHaveLength(1); + expect(links[0].href).toBe( + `/explore?left=${encodeURIComponent( + '{"range":{"from":"now-1h","to":"now"},"datasource":"uid_1","queries":[{"query":"query_1-foo"}],"panelsState":{}}' + )}` + ); + }); + + it('returns internal links when target contains targetField template variable', () => { + const { field, range, dataFrame } = setup({ + title: '', + url: '', + internal: { + query: { query: 'query_1-${__targetField}' }, + datasourceUid: 'uid_1', + datasourceName: 'test_ds', + }, + }); + const links = getFieldLinksForExplore({ field, rowIndex: ROW_WITH_TEXT_VALUE.index, range, dataFrame }); + expect(links).toHaveLength(1); + expect(links[0].href).toBe( + `/explore?left=${encodeURIComponent( + '{"range":{"from":"now-1h","to":"now"},"datasource":"uid_1","queries":[{"query":"query_1-foo"}],"panelsState":{}}' + )}` + ); + }); + + it('returns internal links when target contains field name template variable', () => { + // field cannot be hyphenated, change field name to non-hyphenated + const noHyphenLink = { + title: '', + url: '', + internal: { + query: { query: 'query_1-${fluxDimensions}' }, + datasourceUid: 'uid_1', + datasourceName: 'test_ds', + }, + }; + const { field, range, dataFrame } = setup(noHyphenLink, true, { + name: 'fluxDimensions', + type: FieldType.string, + values: new ArrayVector([ROW_WITH_TEXT_VALUE.value, ROW_WITH_NULL_VALUE.value]), + config: { + links: [noHyphenLink], + }, + }); + const links = getFieldLinksForExplore({ field, rowIndex: ROW_WITH_TEXT_VALUE.index, range, dataFrame }); + expect(links).toHaveLength(1); + expect(links[0].href).toBe( + `/explore?left=${encodeURIComponent( + '{"range":{"from":"now-1h","to":"now"},"datasource":"uid_1","queries":[{"query":"query_1-foo"}],"panelsState":{}}' + )}` + ); + }); + + it('returns internal links when target contains other field name template variables', () => { + // field cannot be hyphenated, change field name to non-hyphenated + const noHyphenLink = { + title: '', + url: '', + internal: { + query: { query: 'query_1-${fluxDimensions}-${fluxDimension2}' }, + datasourceUid: 'uid_1', + datasourceName: 'test_ds', + }, + }; + const { field, range, dataFrame } = setup( + noHyphenLink, + true, + { + name: 'fluxDimensions', + type: FieldType.string, + values: new ArrayVector([ROW_WITH_TEXT_VALUE.value, ROW_WITH_NULL_VALUE.value]), + config: { + links: [noHyphenLink], + }, + }, + [ + { + name: 'fluxDimension2', + type: FieldType.string, + values: new ArrayVector(['foo2', ROW_WITH_NULL_VALUE.value]), + config: { + links: [noHyphenLink], + }, + }, + ] + ); + const links = getFieldLinksForExplore({ field, rowIndex: ROW_WITH_TEXT_VALUE.index, range, dataFrame }); + expect(links).toHaveLength(1); + expect(links[0].href).toBe( + `/explore?left=${encodeURIComponent( + '{"range":{"from":"now-1h","to":"now"},"datasource":"uid_1","queries":[{"query":"query_1-foo-foo2"}],"panelsState":{}}' + )}` + ); }); it('returns no internal links when target contains empty template variables', () => { @@ -165,7 +259,12 @@ describe('getFieldLinksForExplore', () => { const ROW_WITH_TEXT_VALUE = { value: 'foo', index: 0 }; const ROW_WITH_NULL_VALUE = { value: null, index: 1 }; -function setup(link: DataLink, hasAccess = true) { +function setup( + link: DataLink, + hasAccess = true, + fieldOverride?: Field, + dataFrameOtherFieldOverride?: Field[] +) { setLinkSrv({ getDataLinkUIModel(link: DataLink, replaceVariables: InterpolateFunction | undefined, origin: any): LinkModel { return { @@ -196,8 +295,14 @@ function setup(link: DataLink, hasAccess = true) { }, }; + let fieldsArr = [fieldOverride || field]; + + if (dataFrameOtherFieldOverride) { + fieldsArr = [...fieldsArr, ...dataFrameOtherFieldOverride]; + } + const dataFrame: DataFrame = toDataFrame({ - fields: [field], + fields: fieldsArr, }); const range: TimeRange = { @@ -209,5 +314,5 @@ function setup(link: DataLink, hasAccess = true) { }, }; - return { range, field, dataFrame }; + return { range, field: fieldOverride || field, dataFrame }; } diff --git a/public/app/features/explore/utils/links.ts b/public/app/features/explore/utils/links.ts index ad0f27341e0..a8e507a0b59 100644 --- a/public/app/features/explore/utils/links.ts +++ b/public/app/features/explore/utils/links.ts @@ -11,6 +11,7 @@ import { getFieldDisplayValuesProxy, SplitOpen, DataLink, + DisplayValue, } from '@grafana/data'; import { getTemplateSrv } from '@grafana/runtime'; import { contextSrv } from 'app/core/services/context_srv'; @@ -72,19 +73,36 @@ export const getFieldLinksForExplore = (options: { text: 'Raw value', }; + let fieldDisplayValuesProxy: Record | undefined = undefined; + // If we have a dataFrame we can allow referencing other columns and their values in the interpolation. if (dataFrame) { + fieldDisplayValuesProxy = getFieldDisplayValuesProxy({ + frame: dataFrame, + rowIndex, + }); + scopedVars['__data'] = { value: { name: dataFrame.name, refId: dataFrame.refId, - fields: getFieldDisplayValuesProxy({ - frame: dataFrame, - rowIndex, - }), + fields: fieldDisplayValuesProxy, }, text: 'Data', }; + + dataFrame.fields.forEach((f) => { + if (fieldDisplayValuesProxy && fieldDisplayValuesProxy[f.name]) { + scopedVars[f.name] = { + value: fieldDisplayValuesProxy[f.name], + }; + } + }); + + // add this for convenience + scopedVars['__targetField'] = { + value: fieldDisplayValuesProxy[field.name], + }; } if (field.config.links) {