Logs: Derived fields link design update (#23695)

This commit is contained in:
Andrej Ocenas
2020-04-23 20:47:54 +02:00
committed by GitHub
parent 376765b3d7
commit 170a0df194
8 changed files with 159 additions and 67 deletions

View File

@@ -25,22 +25,32 @@ describe('getFieldLinksForExplore', () => {
expect(links[0].title).toBe('external');
});
it('returns generates title for external link', () => {
const { field, range } = setup({
title: '',
url: 'http://regionalhost',
});
const links = getFieldLinksForExplore(field, 0, jest.fn(), range);
expect(links[0].href).toBe('http://regionalhost');
expect(links[0].title).toBe('regionalhost');
});
it('returns correct link model for internal link', () => {
const { field, range } = setup({
title: 'test',
title: '',
url: 'query_1',
meta: {
datasourceUid: 'uid_1',
},
});
const splitfn = jest.fn();
const links = getFieldLinksForExplore(field, 0, splitfn, range);
expect(links[0].href).toBe(
'/explore?left={"range":{"from":"now-1h","to":"now"},"datasource":"test_ds","queries":[{"query":"query_1"}],"mode":"Metrics","ui":{"showingGraph":true,"showingTable":true,"showingLogs":true}}'
);
expect(links[0].title).toBe('test');
expect(links[0].title).toBe('test_ds');
links[0].onClick({});
expect(splitfn).toBeCalledWith({ datasourceUid: 'uid_1', query: 'query_1' });
});

View File

@@ -22,6 +22,10 @@ export function getFieldLinksForExplore(
if (d.link.meta?.datasourceUid) {
return {
...d.linkModel,
title:
d.linkModel.title ||
getDataSourceSrv().getDataSourceSettingsByUid(d.link.meta.datasourceUid)?.name ||
'Unknown datasource',
onClick: () => {
splitOpenFn({
datasourceUid: d.link.meta.datasourceUid,
@@ -37,6 +41,28 @@ export function getFieldLinksForExplore(
href: generateInternalHref(d.link.meta.datasourceUid, d.linkModel.href, range),
};
}
if (!d.linkModel.title) {
let href = d.linkModel.href;
// The URL constructor needs the url to have protocol
if (href.indexOf('://') < 0) {
// Doesn't really matter what protocol we use.
href = `http://${href}`;
}
let title;
try {
const parsedUrl = new URL(href);
title = parsedUrl.hostname;
} catch (_e) {
// Should be good enough fallback, user probably did not input valid url.
title = href;
}
return {
...d.linkModel,
title,
};
}
return d.linkModel;
});
}

View File

@@ -104,7 +104,7 @@ describe('loki result transformer', () => {
});
describe('enhanceDataFrame', () => {
it('', () => {
it('adds links to fields', () => {
const df = new MutableDataFrame({ fields: [{ name: 'line', values: ['nothing', 'trace1=1234', 'trace2=foo'] }] });
enhanceDataFrame(df, {
derivedFields: [
@@ -123,8 +123,15 @@ describe('enhanceDataFrame', () => {
expect(df.fields.length).toBe(3);
const fc = new FieldCache(df);
expect(fc.getFieldByName('trace1').values.toArray()).toEqual([null, '1234', null]);
expect(fc.getFieldByName('trace1').config.links[0]).toEqual({ url: 'http://localhost/${__value.raw}', title: '' });
expect(fc.getFieldByName('trace1').config.links[0]).toEqual({
url: 'http://localhost/${__value.raw}',
title: '',
});
expect(fc.getFieldByName('trace2').values.toArray()).toEqual([null, null, 'foo']);
expect(fc.getFieldByName('trace2').config.links[0]).toEqual({ title: '', meta: { datasourceUid: 'uid' } });
expect(fc.getFieldByName('trace2').config.links[0]).toEqual({
title: '',
meta: { datasourceUid: 'uid' },
});
});
});

View File

@@ -12,6 +12,8 @@ import {
findUniqueLabels,
FieldConfig,
DataFrameView,
DataLink,
Field,
} from '@grafana/data';
import templateSrv from 'app/features/templating/template_srv';
@@ -28,6 +30,7 @@ import {
LokiTailResponse,
LokiQuery,
LokiOptions,
DerivedFieldConfig,
} from './types';
/**
@@ -289,44 +292,50 @@ export const enhanceDataFrame = (dataFrame: DataFrame, config: LokiOptions | nul
if (!derivedFields.length) {
return;
}
const fields = derivedFields.reduce((acc, field) => {
const config: FieldConfig = {};
if (field.url || field.datasourceUid) {
config.links = [
{
url: field.url,
title: '',
meta: field.datasourceUid
? {
datasourceUid: field.datasourceUid,
}
: undefined,
},
];
}
const dataFrameField = {
name: field.name,
type: FieldType.string,
config,
values: new ArrayVector<string>([]),
};
acc[field.name] = dataFrameField;
return acc;
}, {} as Record<string, any>);
const newFields = derivedFields.map(fieldFromDerivedFieldConfig);
const newFieldsMap = _.keyBy(newFields, 'name');
const view = new DataFrameView(dataFrame);
view.forEach((row: { line: string }) => {
for (const field of derivedFields) {
const logMatch = row.line.match(field.matcherRegex);
fields[field.name].values.add(logMatch && logMatch[1]);
newFieldsMap[field.name].values.add(logMatch && logMatch[1]);
}
});
dataFrame.fields = [...dataFrame.fields, ...Object.values(fields)];
dataFrame.fields = [...dataFrame.fields, ...newFields];
};
/**
* Transform derivedField config into dataframe field with config that contains link.
*/
function fieldFromDerivedFieldConfig(derivedFieldConfig: DerivedFieldConfig): Field<any, ArrayVector> {
const config: FieldConfig = {};
if (derivedFieldConfig.url || derivedFieldConfig.datasourceUid) {
const link: Partial<DataLink> = {
// We do not know what title to give here so we count on presentation layer to create a title from metadata.
title: '',
url: derivedFieldConfig.url,
};
// Having field.datasourceUid means it is an internal link.
if (derivedFieldConfig.datasourceUid) {
link.meta = {
datasourceUid: derivedFieldConfig.datasourceUid,
};
}
config.links = [link as DataLink];
}
return {
name: derivedFieldConfig.name,
type: FieldType.string,
config,
// We are adding values later on
values: new ArrayVector<string>([]),
};
}
export function rangeQueryResponseToTimeSeries(
response: LokiResponse,
query: LokiRangeQueryRequest,