Skip to content

Commit 3665dfb

Browse files
tsullivanJoel Griffithelasticmachine
authored
Adds a new config flag to encode with BOM for our CSVs (#63006) (#63265)
* Adds a new config flag to encode with BOM for our CSVs * Push out bom-chars to it's own constant * Getting those snapshots back into shape 💪 Co-authored-by: Elastic Machine <[email protected]> Co-authored-by: Joel Griffith <[email protected]> Co-authored-by: Elastic Machine <[email protected]>
1 parent 1d4706f commit 3665dfb

File tree

6 files changed

+56
-2
lines changed

6 files changed

+56
-2
lines changed

x-pack/legacy/plugins/reporting/__snapshots__/index.test.js.snap

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x-pack/legacy/plugins/reporting/common/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export const API_GENERATE_IMMEDIATE = `${API_BASE_URL_V1}/generate/immediate/csv
1919

2020
export const CONTENT_TYPE_CSV = 'text/csv';
2121
export const CSV_REPORTING_ACTION = 'downloadCsvReport';
22+
export const CSV_BOM_CHARS = '\ufeff';
2223

2324
export const WHITELISTED_JOB_CONTENT_TYPES = [
2425
'application/json',

x-pack/legacy/plugins/reporting/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ export async function config(Joi: any) {
135135
.default(),
136136
}).default(),
137137
csv: Joi.object({
138+
useByteOrderMarkEncoding: Joi.boolean().default(false),
138139
checkForFormulas: Joi.boolean().default(true),
139140
enablePanelActionDownload: Joi.boolean().default(true),
140141
maxSizeBytes: Joi.number()

x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { createMockReportingCore } from '../../../test_helpers';
1313
import { LevelLogger } from '../../../server/lib/level_logger';
1414
import { setFieldFormats } from '../../../server/services';
1515
import { executeJobFactory } from './execute_job';
16+
import { CSV_BOM_CHARS } from '../../../common/constants';
1617

1718
const delay = ms => new Promise(resolve => setTimeout(() => resolve(), ms));
1819

@@ -374,6 +375,50 @@ describe('CSV Execute Job', function() {
374375
});
375376
});
376377

378+
describe('Byte order mark encoding', () => {
379+
it('encodes CSVs with BOM', async () => {
380+
configGetStub.withArgs('csv', 'useByteOrderMarkEncoding').returns(true);
381+
callAsCurrentUserStub.onFirstCall().returns({
382+
hits: {
383+
hits: [{ _source: { one: 'one', two: 'bar' } }],
384+
},
385+
_scroll_id: 'scrollId',
386+
});
387+
388+
const executeJob = await executeJobFactory(mockReportingPlugin, mockLogger);
389+
const jobParams = {
390+
headers: encryptedHeaders,
391+
fields: ['one', 'two'],
392+
conflictedTypesFields: [],
393+
searchRequest: { index: null, body: null },
394+
};
395+
const { content } = await executeJob('job123', jobParams, cancellationToken);
396+
397+
expect(content).toEqual(`${CSV_BOM_CHARS}one,two\none,bar\n`);
398+
});
399+
400+
it('encodes CSVs without BOM', async () => {
401+
configGetStub.withArgs('csv', 'useByteOrderMarkEncoding').returns(false);
402+
callAsCurrentUserStub.onFirstCall().returns({
403+
hits: {
404+
hits: [{ _source: { one: 'one', two: 'bar' } }],
405+
},
406+
_scroll_id: 'scrollId',
407+
});
408+
409+
const executeJob = await executeJobFactory(mockReportingPlugin, mockLogger);
410+
const jobParams = {
411+
headers: encryptedHeaders,
412+
fields: ['one', 'two'],
413+
conflictedTypesFields: [],
414+
searchRequest: { index: null, body: null },
415+
};
416+
const { content } = await executeJob('job123', jobParams, cancellationToken);
417+
418+
expect(content).toEqual('one,two\none,bar\n');
419+
});
420+
});
421+
377422
describe('Elasticsearch call errors', function() {
378423
it('should reject Promise if search call errors out', async function() {
379424
callAsCurrentUserStub.rejects(new Error());

x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import { i18n } from '@kbn/i18n';
88
import Hapi from 'hapi';
99
import { IUiSettingsClient, KibanaRequest } from '../../../../../../../src/core/server';
10-
import { CSV_JOB_TYPE } from '../../../common/constants';
10+
import { CSV_JOB_TYPE, CSV_BOM_CHARS } from '../../../common/constants';
1111
import { ReportingCore } from '../../../server/core';
1212
import { cryptoFactory } from '../../../server/lib';
1313
import { getFieldFormats } from '../../../server/services';
@@ -121,6 +121,8 @@ export const executeJobFactory: ExecuteJobFactory<ESQueueWorkerExecuteFn<
121121
]);
122122

123123
const generateCsv = createGenerateCsv(jobLogger);
124+
const bom = config.get('csv', 'useByteOrderMarkEncoding') ? CSV_BOM_CHARS : '';
125+
124126
const { content, maxSizeReached, size, csvContainsFormulas } = await generateCsv({
125127
searchRequest,
126128
fields,
@@ -139,7 +141,7 @@ export const executeJobFactory: ExecuteJobFactory<ESQueueWorkerExecuteFn<
139141

140142
return {
141143
content_type: 'text/csv',
142-
content,
144+
content: bom + content,
143145
max_size_reached: maxSizeReached,
144146
size,
145147
csv_contains_formulas: csvContainsFormulas,

x-pack/legacy/plugins/reporting/server/config/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ export interface ReportingConfigType {
108108
enablePanelActionDownload: boolean;
109109
checkForFormulas: boolean;
110110
maxSizeBytes: number;
111+
useByteOrderMarkEncoding: boolean;
111112
};
112113
encryptionKey: string;
113114
kibanaServer: any;

0 commit comments

Comments
 (0)