[CSV] Include custom field label in CSV reports (#181565)

- Closes https://github.com/elastic/kibana/issues/100374

## Summary

This PR adds `field.customLabel` to CSV reports (column names) if
available.

Before:
<img width="800" alt="Screenshot 2024-04-24 at 15 16 45"
src="870c9146-dd3a-4cc6-a12f-f67fbd7196f5">

After:
<img width="800" alt="Screenshot 2024-04-24 at 15 17 04"
src="7f870899-71a2-42e7-ade4-d92acca9b96a">


### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Julia Rechkunova 2024-04-26 20:43:04 +02:00 committed by GitHub
parent 41fd6432be
commit 43311754f6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 41 additions and 16 deletions

View file

@ -271,8 +271,8 @@ exports[`CsvGenerator fields from job.searchSource.getFields() (7.12 generated)
`;
exports[`CsvGenerator formats a search result to CSV content 1`] = `
"date,ip,message
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"This is a great message!\\"
"date,ip,message,\\"bytes_custom_label (bytes)\\"
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"This is a great message!\\",100
"
`;

View file

@ -20,6 +20,8 @@ import {
savedObjectsClientMock,
uiSettingsServiceMock,
} from '@kbn/core/server/mocks';
import { createStubDataView } from '@kbn/data-views-plugin/common/data_views/data_view.stub';
import { stubLogstashFieldSpecMap } from '@kbn/data-views-plugin/common/field.stub';
import { ISearchClient, ISearchStartSearchSource } from '@kbn/data-plugin/common';
import { searchSourceInstanceMock } from '@kbn/data-plugin/common/search/search_source/mocks';
import type { IScopedSearchClient } from '@kbn/data-plugin/server';
@ -132,20 +134,31 @@ describe('CsvGenerator', () => {
mockConfig = getMockConfig();
const dataView = createStubDataView({
spec: {
id: 'test',
title: 'logstash-*',
fields: {
...stubLogstashFieldSpecMap,
bytes: {
...stubLogstashFieldSpecMap.bytes,
customLabel: 'bytes_custom_label',
},
},
},
opts: {
metaFields: ['_id', '_index', '_type', '_score'],
},
});
dataView.getFormatterForField = jest.fn();
searchSourceMock.getField = jest.fn((key: string) => {
switch (key) {
case 'pit':
return { id: mockCursorId };
case 'index':
return {
fields: {
getByName: jest.fn(() => []),
getByType: jest.fn(() => []),
},
metaFields: ['_id', '_index', '_type', '_score'],
getFormatterForField: jest.fn(),
getIndexPattern: () => 'logstash-*',
};
return dataView;
}
});
@ -184,13 +197,14 @@ describe('CsvGenerator', () => {
date: `["2020-12-31T00:14:28.000Z"]`,
ip: `["110.135.176.89"]`,
message: `["This is a great message!"]`,
bytes: `[100]`,
},
} as unknown as estypes.SearchHit,
]),
})
);
const generateCsv = new CsvGenerator(
createMockJob({ columns: ['date', 'ip', 'message'] }),
createMockJob({ columns: ['date', 'ip', 'message', 'bytes'] }),
mockConfig,
mockTaskInstanceFields,
{

View file

@ -11,7 +11,7 @@ import type { Writable } from 'stream';
import { errors as esErrors, estypes } from '@elastic/elasticsearch';
import type { IScopedClusterClient, IUiSettingsClient, Logger } from '@kbn/core/server';
import type { ISearchClient, ISearchStartSearchSource } from '@kbn/data-plugin/common';
import type { DataView, ISearchClient, ISearchStartSearchSource } from '@kbn/data-plugin/common';
import { cellHasFormulas, tabifyDocs } from '@kbn/data-plugin/common';
import type { Datatable } from '@kbn/expressions-plugin/server';
import type {
@ -146,11 +146,21 @@ export class CsvGenerator {
private generateHeader(
columns: Set<string>,
builder: MaxSizeStringBuilder,
settings: CsvExportSettings
settings: CsvExportSettings,
dataView: DataView
) {
this.logger.debug(`Building CSV header row`);
const header =
Array.from(columns).map(this.escapeValues(settings)).join(settings.separator) + '\n';
Array.from(columns)
.map((column) => {
const field = dataView?.fields.getByName(column);
if (field && field.customLabel && field.customLabel !== column) {
return `${field.customLabel} (${column})`;
}
return column;
})
.map(this.escapeValues(settings))
.join(settings.separator) + '\n';
if (!builder.tryAppend(header)) {
return {
@ -364,7 +374,7 @@ export class CsvGenerator {
if (first) {
first = false;
this.generateHeader(columns, builder, settings);
this.generateHeader(columns, builder, settings, index);
}
if (table.rows.length < 1) {

View file

@ -29,5 +29,6 @@
"@kbn/es-query",
"@kbn/es-types",
"@kbn/esql-utils",
"@kbn/data-views-plugin",
]
}