logs: restrict permalinks to datasources with id fields (#71729)

* logs: restrict permalinks to datasources with id fields

* simplified code

* more tests

* more test

* removed unnecessary comment

* fixed tests

* updated tests
This commit is contained in:
Gábor Farkas
2023-07-17 15:01:48 +02:00
committed by GitHub
parent ab58466d09
commit d0351bac57
7 changed files with 192 additions and 9 deletions
+3
View File
@@ -55,6 +55,9 @@ export interface LogRowModel {
// the same as rows final index when rendered.
rowIndex: number;
// The value of the the dataframe's id field, if it exists
rowId?: string;
// Full DataFrame from which we parsed this log.
// TODO: refactor this so we do not need to pass whole dataframes in addition to also parsed data.
dataFrame: DataFrame;
@@ -97,9 +97,9 @@ describe('Logs', () => {
const getComponent = (partialProps?: Partial<ComponentProps<typeof Logs>>, logs?: LogRowModel[]) => {
const rows = [
makeLog({ uid: '1', timeEpochMs: 1 }),
makeLog({ uid: '2', timeEpochMs: 2 }),
makeLog({ uid: '3', timeEpochMs: 3 }),
makeLog({ uid: '1', rowId: 'id1', timeEpochMs: 1 }),
makeLog({ uid: '2', rowId: 'id2', timeEpochMs: 2 }),
makeLog({ uid: '3', rowId: 'id3', timeEpochMs: 3 }),
];
const testDataFrame = {
@@ -374,6 +374,14 @@ class UnthemedLogs extends PureComponent<Props, State> {
};
onPermalinkClick = async (row: LogRowModel) => {
// this is an extra check, to be sure that we are not
// creating permalinks for logs without an id-field.
// normally it should never happen, because we do not
// display the permalink button in such cases.
if (row.rowId === undefined) {
return;
}
// get explore state, add log-row-id and make timerange absolute
const urlState = getUrlStateFromPaneState(getState().explore.panes[this.props.exploreId]!);
urlState.panelsState = { ...this.props.panelState, logs: { id: row.uid } };
@@ -102,7 +102,7 @@ export const LogRowMenuCell = React.memo(
aria-label="Pin line"
/>
)}
{onPermalinkClick && row.uid && (
{onPermalinkClick && row.rowId !== undefined && row.uid && (
<IconButton
tooltip="Copy shortlink"
aria-label="Copy shortlink"
@@ -66,12 +66,18 @@ describe('LogRowMessage', () => {
});
describe('with permalinking', () => {
it('should show permalinking button when `onPermalinkClick` is defined', async () => {
setup({ onPermalinkClick: jest.fn() });
it('should show permalinking button when `onPermalinkClick` is defined and rowId is defined', async () => {
setup({ onPermalinkClick: jest.fn() }, { rowId: 'id1' });
await userEvent.hover(screen.getByText('test123'));
expect(screen.queryByLabelText('Copy shortlink')).toBeInTheDocument();
});
it('should not show permalinking button when `onPermalinkClick` is defined and rowId is not defined', async () => {
setup({ onPermalinkClick: jest.fn() });
await userEvent.hover(screen.getByText('test123'));
expect(screen.queryByLabelText('Copy shortlink')).not.toBeInTheDocument();
});
it('should not show permalinking button when `onPermalinkClick` is not defined', () => {
setup();
expect(screen.queryByLabelText('Copy shortlink')).not.toBeInTheDocument();
@@ -79,7 +85,7 @@ describe('LogRowMessage', () => {
it('should call `onPermalinkClick` with row on click', async () => {
const permalinkClick = jest.fn();
const props = setup({ onPermalinkClick: permalinkClick });
const props = setup({ onPermalinkClick: permalinkClick }, { rowId: 'id1' });
await userEvent.hover(screen.getByText('test123'));
const button = screen.getByLabelText('Copy shortlink');
+8 -2
View File
@@ -424,7 +424,7 @@ export function logSeriesToLogsModel(logSeries: DataFrame[], queries: DataQuery[
const datasourceType = queries.find((query) => query.refId === series.refId)?.datasource?.type;
rows.push({
const row: LogRowModel = {
entryFieldIndex: stringField.index,
rowIndex: j,
dataFrame: series,
@@ -444,7 +444,13 @@ export function logSeriesToLogsModel(logSeries: DataFrame[], queries: DataQuery[
// prepend refId to uid to make it unique across all series in a case when series contain duplicates
uid: `${series.refId}_${idField ? idField.values[j] : j.toString()}`,
datasourceType,
});
};
if (idField !== null) {
row.rowId = idField.values[j];
}
rows.push(row);
}
}
@@ -137,6 +137,7 @@ describe('logSeriesToLogsModel should parse different logs-dataframe formats', (
rows: [
{
dataFrame: frames[0],
rowId: 'id1',
datasourceType: undefined,
entry: 'line1',
entryFieldIndex: 1,
@@ -164,6 +165,7 @@ describe('logSeriesToLogsModel should parse different logs-dataframe formats', (
},
{
dataFrame: frames[1],
rowId: 'id2',
datasourceType: undefined,
entry: 'line2',
entryFieldIndex: 1,
@@ -191,6 +193,7 @@ describe('logSeriesToLogsModel should parse different logs-dataframe formats', (
},
{
dataFrame: frames[2],
rowId: 'id3',
datasourceType: undefined,
entry: 'line3',
entryFieldIndex: 1,
@@ -290,6 +293,7 @@ describe('logSeriesToLogsModel should parse different logs-dataframe formats', (
rows: [
{
dataFrame: frames[0],
rowId: 'id1',
datasourceType: undefined,
entry: 'line1',
entryFieldIndex: 2,
@@ -318,6 +322,7 @@ describe('logSeriesToLogsModel should parse different logs-dataframe formats', (
},
{
dataFrame: frames[0],
rowId: 'id2',
datasourceType: undefined,
entry: 'line2',
entryFieldIndex: 2,
@@ -346,6 +351,7 @@ describe('logSeriesToLogsModel should parse different logs-dataframe formats', (
},
{
dataFrame: frames[0],
rowId: 'id3',
datasourceType: undefined,
entry: 'line3',
entryFieldIndex: 2,
@@ -925,4 +931,158 @@ describe('logSeriesToLogsModel should parse different logs-dataframe formats', (
expect(logSeriesToLogsModel(frames)).toStrictEqual(expected);
});
it('should add rowId when id field exists)', () => {
const frames: DataFrame[] = [
{
refId: 'A',
fields: [
{
name: 'Time',
type: FieldType.time,
config: {},
values: [1686142519756, 1686142520411],
nanos: [641000, 0],
},
{
name: 'Line',
type: FieldType.string,
config: {},
values: ['line1', 'line2'],
},
{
name: 'id',
type: FieldType.string,
config: {},
values: ['id1', 'id2'],
},
],
length: 2,
},
];
const expected = {
hasUniqueLabels: false,
meta: [],
rows: [
{
dataFrame: frames[0],
rowId: 'id1',
datasourceType: undefined,
entry: 'line1',
entryFieldIndex: 1,
hasAnsi: false,
hasUnescapedContent: false,
labels: {},
logLevel: 'unknown',
raw: 'line1',
rowIndex: 0,
searchWords: [],
timeEpochMs: 1686142519756,
timeEpochNs: '1686142519756641000',
timeFromNow: 'mock:dateTimeFormatTimeAgo:2023-06-07T06:55:19-06:00',
timeLocal: '2023-06-07 06:55:19',
timeUtc: '2023-06-07 12:55:19',
uid: 'A_id1',
uniqueLabels: {},
},
{
dataFrame: frames[0],
rowId: 'id2',
datasourceType: undefined,
entry: 'line2',
entryFieldIndex: 1,
hasAnsi: false,
hasUnescapedContent: false,
labels: {},
logLevel: 'unknown',
raw: 'line2',
rowIndex: 1,
searchWords: [],
timeEpochMs: 1686142520411,
timeEpochNs: '1686142520411000000',
timeFromNow: 'mock:dateTimeFormatTimeAgo:2023-06-07T06:55:20-06:00',
timeLocal: '2023-06-07 06:55:20',
timeUtc: '2023-06-07 12:55:20',
uid: 'A_id2',
uniqueLabels: {},
},
],
};
expect(logSeriesToLogsModel(frames)).toStrictEqual(expected);
});
it('should not add rowId when id field does not exist)', () => {
const frames: DataFrame[] = [
{
refId: 'A',
fields: [
{
name: 'Time',
type: FieldType.time,
config: {},
values: [1686142519756, 1686142520411],
nanos: [641000, 0],
},
{
name: 'Line',
type: FieldType.string,
config: {},
values: ['line1', 'line2'],
},
],
length: 2,
},
];
const expected = {
hasUniqueLabels: false,
meta: [],
rows: [
{
dataFrame: frames[0],
datasourceType: undefined,
entry: 'line1',
entryFieldIndex: 1,
hasAnsi: false,
hasUnescapedContent: false,
labels: {},
logLevel: 'unknown',
raw: 'line1',
rowIndex: 0,
searchWords: [],
timeEpochMs: 1686142519756,
timeEpochNs: '1686142519756641000',
timeFromNow: 'mock:dateTimeFormatTimeAgo:2023-06-07T06:55:19-06:00',
timeLocal: '2023-06-07 06:55:19',
timeUtc: '2023-06-07 12:55:19',
uid: 'A_0',
uniqueLabels: {},
},
{
dataFrame: frames[0],
datasourceType: undefined,
entry: 'line2',
entryFieldIndex: 1,
hasAnsi: false,
hasUnescapedContent: false,
labels: {},
logLevel: 'unknown',
raw: 'line2',
rowIndex: 1,
searchWords: [],
timeEpochMs: 1686142520411,
timeEpochNs: '1686142520411000000',
timeFromNow: 'mock:dateTimeFormatTimeAgo:2023-06-07T06:55:20-06:00',
timeLocal: '2023-06-07 06:55:20',
timeUtc: '2023-06-07 12:55:20',
uid: 'A_1',
uniqueLabels: {},
},
],
};
expect(logSeriesToLogsModel(frames)).toStrictEqual(expected);
});
});