From f3dc980bb92d450b45bf344f4ae10ef11ff864ed Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 13 Sep 2021 16:03:15 +0200 Subject: [PATCH 1/6] [Discover] Fix permissions for runtime fields functional test (#111925) --- test/functional/apps/discover/_runtime_fields_editor.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/functional/apps/discover/_runtime_fields_editor.ts b/test/functional/apps/discover/_runtime_fields_editor.ts index fa42b0ac49617..642743d3a0377 100644 --- a/test/functional/apps/discover/_runtime_fields_editor.ts +++ b/test/functional/apps/discover/_runtime_fields_editor.ts @@ -15,6 +15,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const esArchiver = getService('esArchiver'); const fieldEditor = getService('fieldEditor'); + const security = getService('security'); const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']); const defaultSettings = { defaultIndex: 'logstash-*', @@ -32,6 +33,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('discover integration with runtime fields editor', function describeIndexTests() { before(async function () { + await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); await kibanaServer.uiSettings.replace(defaultSettings); @@ -40,6 +42,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); after(async () => { + await security.testUser.restoreDefaults(); await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); await kibanaServer.savedObjects.clean({ types: ['saved-search'] }); }); From 22d253f8467f73121b78c61f50e75df4db1f2690 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Mon, 13 Sep 2021 16:44:03 +0200 Subject: [PATCH 2/6] [Discover] Fix a functional test 'index pattern with unmapped fields' (#111323) * [Discover] Fix a functional test * Fix failing type * Select index pattern first * Select index pattern first Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../apps/discover/_indexpattern_with_unmapped_fields.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/functional/apps/discover/_indexpattern_with_unmapped_fields.ts b/test/functional/apps/discover/_indexpattern_with_unmapped_fields.ts index 264885490cdfc..2a1e60db541e8 100644 --- a/test/functional/apps/discover/_indexpattern_with_unmapped_fields.ts +++ b/test/functional/apps/discover/_indexpattern_with_unmapped_fields.ts @@ -30,6 +30,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); await PageObjects.common.navigateToApp('discover'); + await PageObjects.discover.selectIndexPattern('test-index-unmapped-fields'); }); after(async () => { From ef65bcb2674e64c9b213ac24057616ed0e984589 Mon Sep 17 00:00:00 2001 From: Khristinin Nikita Date: Mon, 13 Sep 2021 16:55:49 +0200 Subject: [PATCH 3/6] Remove fullWidth for enrichment range picekr (#111502) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../event_details/cti_details/enrichment_range_picker.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/enrichment_range_picker.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/enrichment_range_picker.tsx index bc66f56667f16..3889823c3fc69 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/enrichment_range_picker.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/enrichment_range_picker.tsx @@ -48,9 +48,8 @@ export const EnrichmentRangePicker: React.FC = ({ range, setRa return ( - + Date: Mon, 13 Sep 2021 17:01:51 +0200 Subject: [PATCH 4/6] [FieldFormats] Reduce `any` usage (#111530) --- .../fields/index_pattern_field.test.ts | 4 +- .../index_patterns/index_pattern.test.ts | 8 +-- .../search/search_source/search_source.ts | 2 +- .../doc_table/lib/row_formatter.tsx | 2 +- .../discover_grid/get_render_cell_value.tsx | 2 +- .../common/content_types/html_content_type.ts | 2 +- .../common/converters/boolean.test.ts | 2 +- .../common/converters/boolean.ts | 2 +- .../common/converters/bytes.test.ts | 9 +-- .../field_formats/common/converters/color.tsx | 6 +- .../converters/date_nanos_shared.test.ts | 16 +++-- .../common/converters/date_nanos_shared.ts | 21 ++++-- .../common/converters/duration.test.ts | 4 +- .../common/converters/duration.ts | 2 +- .../common/converters/histogram.ts | 2 +- .../common/converters/ip.test.ts | 2 +- .../field_formats/common/converters/ip.ts | 4 +- .../common/converters/number.test.ts | 6 +- .../common/converters/numeral.ts | 4 +- .../common/converters/percent.test.ts | 6 +- .../common/converters/percent.ts | 2 +- .../common/converters/relative_date.ts | 4 +- .../common/converters/source.tsx | 16 ++--- .../common/converters/static_lookup.ts | 10 +-- .../field_formats/common/converters/string.ts | 14 ++-- .../common/converters/truncate.ts | 2 +- .../field_formats/common/converters/url.ts | 15 ++-- .../field_formats/common/field_format.test.ts | 5 +- .../field_formats/common/field_format.ts | 35 +++++---- .../common/field_formats_registry.test.ts | 32 +++------ .../common/field_formats_registry.ts | 67 ++++++++++------- src/plugins/field_formats/common/index.ts | 9 ++- src/plugins/field_formats/common/mocks.ts | 4 +- src/plugins/field_formats/common/types.ts | 72 ++++++++++++++----- .../common/utils/as_pretty_string.ts | 2 +- .../common/utils/highlight/highlight_html.ts | 5 +- .../utils/highlight/highlight_request.test.ts | 9 +-- .../utils/highlight/highlight_request.ts | 2 +- .../common/utils/shorten_dotted_string.ts | 4 +- .../public/lib/converters/date.test.ts | 16 +++-- .../public/lib/converters/date.ts | 4 +- .../lib/converters/date_nanos_server.test.ts | 13 ++-- .../lib/converters/date_nanos_server.ts | 10 +-- .../server/lib/converters/date_server.ts | 15 ++-- .../field_formats/server/plugin.test.ts | 11 +-- .../field_formats/server/ui_settings.ts | 7 +- .../editors/duration/duration.tsx | 16 +++-- .../editors/string/string.tsx | 12 ++-- .../field_format_editor/editors/url/url.tsx | 5 +- .../public/indexpattern_datasource/types.ts | 3 +- 50 files changed, 310 insertions(+), 217 deletions(-) diff --git a/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts b/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts index f95de90955b65..51687cfd82600 100644 --- a/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts +++ b/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts @@ -153,14 +153,14 @@ describe('Field', function () { it('spec snapshot', () => { const field = new IndexPatternField(fieldValues); const getFormatterForField = () => - ({ + (({ toJSON: () => ({ id: 'number', params: { pattern: '$0,0.[00]', }, }), - } as FieldFormat); + } as unknown) as FieldFormat); expect(field.toSpec({ getFormatterForField })).toMatchSnapshot(); }); }); diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts index f6be2bd9a8685..1d97c6fa413ee 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts @@ -289,17 +289,17 @@ describe('IndexPattern', () => { describe('toSpec', () => { test('should match snapshot', () => { - const formatter = { + const formatter = ({ toJSON: () => ({ id: 'number', params: { pattern: '$0,0.[00]' } }), - } as FieldFormat; + } as unknown) as FieldFormat; indexPattern.getFormatterForField = () => formatter; expect(indexPattern.toSpec()).toMatchSnapshot(); }); test('can restore from spec', async () => { - const formatter = { + const formatter = ({ toJSON: () => ({ id: 'number', params: { pattern: '$0,0.[00]' } }), - } as FieldFormat; + } as unknown) as FieldFormat; indexPattern.getFormatterForField = () => formatter; const spec = indexPattern.toSpec(); const restoredPattern = new IndexPattern({ diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index 56a74994c71ca..f0bcf6446c14c 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -828,7 +828,7 @@ export class SearchSource { body.query = buildEsQuery(index, query, filters, esQueryConfigs); if (highlightAll && body.query) { - body.highlight = getHighlightRequest(body.query, getConfig(UI_SETTINGS.DOC_HIGHLIGHT)); + body.highlight = getHighlightRequest(getConfig(UI_SETTINGS.DOC_HIGHLIGHT)); delete searchRequest.highlightAll; } diff --git a/src/plugins/discover/public/application/apps/main/components/doc_table/lib/row_formatter.tsx b/src/plugins/discover/public/application/apps/main/components/doc_table/lib/row_formatter.tsx index ae3f1cd0057cc..3b2d6d1c793ae 100644 --- a/src/plugins/discover/public/application/apps/main/components/doc_table/lib/row_formatter.tsx +++ b/src/plugins/discover/public/application/apps/main/components/doc_table/lib/row_formatter.tsx @@ -73,7 +73,7 @@ export const formatTopLevelObject = ( const displayKey = fields.getByName ? fields.getByName(key)?.displayName : undefined; const formatter = field ? indexPattern.getFormatterForField(field) - : { convert: (v: string, ...rest: unknown[]) => String(v) }; + : { convert: (v: unknown, ...rest: unknown[]) => String(v) }; if (!values.map) return; const formatted = values .map((val: unknown) => diff --git a/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx index ccbcc31e154c9..f62b8b411e2bf 100644 --- a/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx @@ -89,7 +89,7 @@ export const getRenderCellValueFn = ( : undefined; const formatter = subField ? indexPattern.getFormatterForField(subField) - : { convert: (v: string, ...rest: unknown[]) => String(v) }; + : { convert: (v: unknown, ...rest: unknown[]) => String(v) }; const formatted = (values as unknown[]) .map((val: unknown) => formatter.convert(val, 'html', { diff --git a/src/plugins/field_formats/common/content_types/html_content_type.ts b/src/plugins/field_formats/common/content_types/html_content_type.ts index 71ac293b7717b..3b7a48a9329a6 100644 --- a/src/plugins/field_formats/common/content_types/html_content_type.ts +++ b/src/plugins/field_formats/common/content_types/html_content_type.ts @@ -43,7 +43,7 @@ export const setup = ( return convert.call(format, value, options); } - const subValues = value.map((v: any) => recurse(v, options)); + const subValues = value.map((v: unknown) => recurse(v, options)); const useMultiLine = subValues.some((sub: string) => sub.indexOf('\n') > -1); return subValues.join(',' + (useMultiLine ? '\n' : ' ')); diff --git a/src/plugins/field_formats/common/converters/boolean.test.ts b/src/plugins/field_formats/common/converters/boolean.test.ts index a44c760940b40..4bc045f568998 100644 --- a/src/plugins/field_formats/common/converters/boolean.test.ts +++ b/src/plugins/field_formats/common/converters/boolean.test.ts @@ -9,7 +9,7 @@ import { BoolFormat } from './boolean'; describe('Boolean Format', () => { - let boolean: Record; + let boolean: BoolFormat; beforeEach(() => { boolean = new BoolFormat({}, jest.fn()); diff --git a/src/plugins/field_formats/common/converters/boolean.ts b/src/plugins/field_formats/common/converters/boolean.ts index 7019b6423b89e..81fd419c9861e 100644 --- a/src/plugins/field_formats/common/converters/boolean.ts +++ b/src/plugins/field_formats/common/converters/boolean.ts @@ -20,7 +20,7 @@ export class BoolFormat extends FieldFormat { }); static fieldType = [KBN_FIELD_TYPES.BOOLEAN, KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.STRING]; - textConvert: TextContextTypeConvert = (value) => { + textConvert: TextContextTypeConvert = (value: string | number | boolean) => { if (typeof value === 'string') { value = value.trim().toLowerCase(); } diff --git a/src/plugins/field_formats/common/converters/bytes.test.ts b/src/plugins/field_formats/common/converters/bytes.test.ts index a820b2c1f2b32..895129a74da3c 100644 --- a/src/plugins/field_formats/common/converters/bytes.test.ts +++ b/src/plugins/field_formats/common/converters/bytes.test.ts @@ -8,13 +8,14 @@ import { BytesFormat } from './bytes'; import { FORMATS_UI_SETTINGS } from '../constants/ui_settings'; +import { FieldFormatsGetConfigFn } from '../types'; describe('BytesFormat', () => { - const config: Record = {}; + const config: { [key: string]: string } = { + [FORMATS_UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN]: '0,0.[000]b', + }; - config[FORMATS_UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN] = '0,0.[000]b'; - - const getConfig = (key: string) => config[key]; + const getConfig: FieldFormatsGetConfigFn = (key: string) => config[key]; test('default pattern', () => { const formatter = new BytesFormat({}, getConfig); diff --git a/src/plugins/field_formats/common/converters/color.tsx b/src/plugins/field_formats/common/converters/color.tsx index 417c2481e626b..3e5ff97830479 100644 --- a/src/plugins/field_formats/common/converters/color.tsx +++ b/src/plugins/field_formats/common/converters/color.tsx @@ -31,11 +31,11 @@ export class ColorFormat extends FieldFormat { }; } - findColorRuleForVal(val: any) { + findColorRuleForVal(val: string | number) { switch (this.param('fieldType')) { case 'string': return findLast(this.param('colors'), (colorParam: typeof DEFAULT_CONVERTER_COLOR) => { - return new RegExp(colorParam.regex).test(val); + return new RegExp(colorParam.regex).test(val as string); }); case 'number': @@ -50,7 +50,7 @@ export class ColorFormat extends FieldFormat { } } - htmlConvert: HtmlContextTypeConvert = (val) => { + htmlConvert: HtmlContextTypeConvert = (val: string | number) => { const color = this.findColorRuleForVal(val) as typeof DEFAULT_CONVERTER_COLOR; const displayVal = escape(asPrettyString(val)); diff --git a/src/plugins/field_formats/common/converters/date_nanos_shared.test.ts b/src/plugins/field_formats/common/converters/date_nanos_shared.test.ts index 6f0b28cc83f55..4b9e82547bbb8 100644 --- a/src/plugins/field_formats/common/converters/date_nanos_shared.test.ts +++ b/src/plugins/field_formats/common/converters/date_nanos_shared.test.ts @@ -8,17 +8,23 @@ import moment from 'moment-timezone'; import { DateNanosFormat, analysePatternForFract, formatWithNanos } from './date_nanos_shared'; +import { FieldFormatsGetConfigFn } from '../types'; describe('Date Nanos Format', () => { let convert: Function; - let mockConfig: Record; + let mockConfig: { + dateNanosFormat: string; + 'dateFormat:tz': string; + [other: string]: string; + }; beforeEach(() => { - mockConfig = {}; - mockConfig.dateNanosFormat = 'MMMM Do YYYY, HH:mm:ss.SSSSSSSSS'; - mockConfig['dateFormat:tz'] = 'Browser'; + mockConfig = { + dateNanosFormat: 'MMMM Do YYYY, HH:mm:ss.SSSSSSSSS', + 'dateFormat:tz': 'Browser', + }; - const getConfig = (key: string) => mockConfig[key]; + const getConfig: FieldFormatsGetConfigFn = (key: string) => mockConfig[key]; const date = new DateNanosFormat({}, getConfig); convert = date.convert.bind(date); diff --git a/src/plugins/field_formats/common/converters/date_nanos_shared.ts b/src/plugins/field_formats/common/converters/date_nanos_shared.ts index 1d226c936a977..daffc289a7c7b 100644 --- a/src/plugins/field_formats/common/converters/date_nanos_shared.ts +++ b/src/plugins/field_formats/common/converters/date_nanos_shared.ts @@ -13,20 +13,27 @@ import moment, { Moment } from 'moment'; import { FieldFormat, FIELD_FORMAT_IDS } from '../'; import { TextContextTypeConvert } from '../types'; +interface FractPatternObject { + length: number; + patternNanos: string; + pattern: string; + patternEscaped: string; +} + /** * Analyse the given moment.js format pattern for the fractional sec part (S,SS,SSS...) * returning length, match, pattern and an escaped pattern, that excludes the fractional * part when formatting with moment.js -> e.g. [SSS] */ -export function analysePatternForFract(pattern: string) { - const fracSecMatch = pattern.match('S+') as any; // extract fractional seconds sub-pattern +export function analysePatternForFract(pattern: string): FractPatternObject { + const fracSecMatch = pattern.match('S+'); // extract fractional seconds sub-pattern const fracSecMatchStr = fracSecMatch ? fracSecMatch[0] : ''; return { length: fracSecMatchStr.length, patternNanos: fracSecMatchStr, pattern, - patternEscaped: fracSecMatchStr ? pattern.replace(fracSecMatch, `[${fracSecMatch}]`) : '', + patternEscaped: fracSecMatchStr ? pattern.replace(fracSecMatchStr, `[${fracSecMatchStr}]`) : '', }; } @@ -38,7 +45,7 @@ export function analysePatternForFract(pattern: string) { export function formatWithNanos( dateMomentObj: Moment, valRaw: string, - fracPatternObj: Record + fracPatternObj: FractPatternObject ) { if (fracPatternObj.length <= 3) { // S,SS,SSS is formatted correctly by moment.js @@ -77,7 +84,7 @@ export class DateNanosFormat extends FieldFormat { }; } - textConvert: TextContextTypeConvert = (val) => { + textConvert: TextContextTypeConvert = (val: string | number) => { // don't give away our ref to converter so // we can hot-swap when config changes const pattern = this.param('pattern'); @@ -91,7 +98,7 @@ export class DateNanosFormat extends FieldFormat { this.timeZone = timezone; this.memoizedPattern = pattern; - this.memoizedConverter = memoize(function converter(value: any) { + this.memoizedConverter = memoize(function converter(value: string | number) { if (value === null || value === undefined) { return '-'; } @@ -102,7 +109,7 @@ export class DateNanosFormat extends FieldFormat { // fallback for max/min aggregation, where unixtime in ms is returned as a number // aggregations in Elasticsearch generally just return ms return date.format(fallbackPattern); - } else if (date.isValid()) { + } else if (date.isValid() && typeof value === 'string') { return formatWithNanos(date, value, fractPattern); } else { return value; diff --git a/src/plugins/field_formats/common/converters/duration.test.ts b/src/plugins/field_formats/common/converters/duration.test.ts index 9ea9919e757de..58e3fb4f3a967 100644 --- a/src/plugins/field_formats/common/converters/duration.test.ts +++ b/src/plugins/field_formats/common/converters/duration.test.ts @@ -315,9 +315,9 @@ describe('Duration Format', () => { showSuffix: boolean | undefined; useShortSuffix?: boolean; includeSpaceWithSuffix?: boolean; - fixtures: any[]; + fixtures: Array<{ input: number; output: string }>; }) { - fixtures.forEach((fixture: Record) => { + fixtures.forEach((fixture: { input: number; output: string }) => { const input = fixture.input; const output = fixture.output; diff --git a/src/plugins/field_formats/common/converters/duration.ts b/src/plugins/field_formats/common/converters/duration.ts index a92a6756b335c..c913ee03d9d36 100644 --- a/src/plugins/field_formats/common/converters/duration.ts +++ b/src/plugins/field_formats/common/converters/duration.ts @@ -235,7 +235,7 @@ export class DurationFormat extends FieldFormat { }; } - textConvert: TextContextTypeConvert = (val) => { + textConvert: TextContextTypeConvert = (val: number) => { const inputFormat = this.param('inputFormat'); const outputFormat = this.param('outputFormat') as keyof Duration; const outputPrecision = this.param('outputPrecision'); diff --git a/src/plugins/field_formats/common/converters/histogram.ts b/src/plugins/field_formats/common/converters/histogram.ts index 52ddf1bd4ed22..2f6928b2abd8e 100644 --- a/src/plugins/field_formats/common/converters/histogram.ts +++ b/src/plugins/field_formats/common/converters/histogram.ts @@ -34,7 +34,7 @@ export class HistogramFormat extends FieldFormat { }; } - textConvert: TextContextTypeConvert = (val) => { + textConvert: TextContextTypeConvert = (val: number) => { if (typeof val === 'number') { const subFormatId = this.param('id'); const SubFormat = diff --git a/src/plugins/field_formats/common/converters/ip.test.ts b/src/plugins/field_formats/common/converters/ip.test.ts index b4c39d1c31d66..0c07c72d22375 100644 --- a/src/plugins/field_formats/common/converters/ip.test.ts +++ b/src/plugins/field_formats/common/converters/ip.test.ts @@ -9,7 +9,7 @@ import { IpFormat } from './ip'; describe('IP Address Format', () => { - let ip: Record; + let ip: IpFormat; beforeEach(() => { ip = new IpFormat({}, jest.fn()); diff --git a/src/plugins/field_formats/common/converters/ip.ts b/src/plugins/field_formats/common/converters/ip.ts index ad49d679a3526..34fac5e1de2af 100644 --- a/src/plugins/field_formats/common/converters/ip.ts +++ b/src/plugins/field_formats/common/converters/ip.ts @@ -19,9 +19,9 @@ export class IpFormat extends FieldFormat { }); static fieldType = KBN_FIELD_TYPES.IP; - textConvert: TextContextTypeConvert = (val) => { + textConvert: TextContextTypeConvert = (val: number) => { if (val === undefined || val === null) return '-'; - if (!isFinite(val)) return val; + if (!isFinite(val)) return String(val); // shazzam! // eslint-disable-next-line no-bitwise diff --git a/src/plugins/field_formats/common/converters/number.test.ts b/src/plugins/field_formats/common/converters/number.test.ts index da849fae1e6ab..379eca73baffc 100644 --- a/src/plugins/field_formats/common/converters/number.test.ts +++ b/src/plugins/field_formats/common/converters/number.test.ts @@ -10,9 +10,9 @@ import { NumberFormat } from './number'; import { FORMATS_UI_SETTINGS } from '../constants/ui_settings'; describe('NumberFormat', () => { - const config: Record = {}; - - config[FORMATS_UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN] = '0,0.[000]'; + const config: { [key: string]: string } = { + [FORMATS_UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN]: '0,0.[000]', + }; const getConfig = (key: string) => config[key]; diff --git a/src/plugins/field_formats/common/converters/numeral.ts b/src/plugins/field_formats/common/converters/numeral.ts index 7f11b6377836d..05c512ba8856f 100644 --- a/src/plugins/field_formats/common/converters/numeral.ts +++ b/src/plugins/field_formats/common/converters/numeral.ts @@ -17,7 +17,7 @@ import { FORMATS_UI_SETTINGS } from '../constants/ui_settings'; const numeralInst = numeral(); -numeralLanguages.forEach((numeralLanguage: Record) => { +numeralLanguages.forEach((numeralLanguage: Record) => { numeral.language(numeralLanguage.id, numeralLanguage.lang); }); @@ -31,7 +31,7 @@ export abstract class NumeralFormat extends FieldFormat { pattern: this.getConfig!(`format:${this.id}:defaultPattern`), }); - protected getConvertedValue(val: any): string { + protected getConvertedValue(val: number | string): string { if (val === -Infinity) return '-∞'; if (val === +Infinity) return '+∞'; if (typeof val !== 'number') { diff --git a/src/plugins/field_formats/common/converters/percent.test.ts b/src/plugins/field_formats/common/converters/percent.test.ts index d01acf571f9d9..50ea3b9f4fd61 100644 --- a/src/plugins/field_formats/common/converters/percent.test.ts +++ b/src/plugins/field_formats/common/converters/percent.test.ts @@ -10,9 +10,9 @@ import { PercentFormat } from './percent'; import { FORMATS_UI_SETTINGS } from '../constants/ui_settings'; describe('PercentFormat', () => { - const config: Record = {}; - - config[FORMATS_UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN] = '0,0.[000]%'; + const config: { [key: string]: string } = { + [FORMATS_UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN]: '0,0.[000]%', + }; const getConfig = (key: string) => config[key]; diff --git a/src/plugins/field_formats/common/converters/percent.ts b/src/plugins/field_formats/common/converters/percent.ts index 497fb088cd416..f2210993dea31 100644 --- a/src/plugins/field_formats/common/converters/percent.ts +++ b/src/plugins/field_formats/common/converters/percent.ts @@ -27,7 +27,7 @@ export class PercentFormat extends NumeralFormat { fractional: true, }); - textConvert: TextContextTypeConvert = (val) => { + textConvert: TextContextTypeConvert = (val: string | number) => { const formatted = super.getConvertedValue(val); if (this.param('fractional')) { diff --git a/src/plugins/field_formats/common/converters/relative_date.ts b/src/plugins/field_formats/common/converters/relative_date.ts index cdc54479b150b..0d66c372e089f 100644 --- a/src/plugins/field_formats/common/converters/relative_date.ts +++ b/src/plugins/field_formats/common/converters/relative_date.ts @@ -20,7 +20,7 @@ export class RelativeDateFormat extends FieldFormat { }); static fieldType = KBN_FIELD_TYPES.DATE; - textConvert: TextContextTypeConvert = (val) => { + textConvert: TextContextTypeConvert = (val: string | number) => { if (val === null || val === undefined) { return '-'; } @@ -29,7 +29,7 @@ export class RelativeDateFormat extends FieldFormat { if (date.isValid()) { return date.fromNow(); } else { - return val; + return String(val); } }; } diff --git a/src/plugins/field_formats/common/converters/source.tsx b/src/plugins/field_formats/common/converters/source.tsx index 9fa7738cabee2..1caffb5bfb9a8 100644 --- a/src/plugins/field_formats/common/converters/source.tsx +++ b/src/plugins/field_formats/common/converters/source.tsx @@ -41,9 +41,9 @@ export class SourceFormat extends FieldFormat { static title = '_source'; static fieldType = KBN_FIELD_TYPES._SOURCE; - textConvert: TextContextTypeConvert = (value) => JSON.stringify(value); + textConvert: TextContextTypeConvert = (value: string) => JSON.stringify(value); - htmlConvert: HtmlContextTypeConvert = (value, options = {}) => { + htmlConvert: HtmlContextTypeConvert = (value: string, options = {}) => { const { field, hit, indexPattern } = options; if (!field) { @@ -52,18 +52,18 @@ export class SourceFormat extends FieldFormat { return escape(converter(value)); } - const highlights = (hit && hit.highlight) || {}; + const highlights: Record = (hit && hit.highlight) || {}; // TODO: remove index pattern dependency - const formatted = indexPattern.formatHit(hit); - const highlightPairs: any[] = []; - const sourcePairs: any[] = []; + const formatted = hit ? indexPattern!.formatHit(hit) : {}; + const highlightPairs: Array<[string, string]> = []; + const sourcePairs: Array<[string, string]> = []; const isShortDots = this.getConfig!(FORMATS_UI_SETTINGS.SHORT_DOTS_ENABLE); keys(formatted).forEach((key) => { const pairs = highlights[key] ? highlightPairs : sourcePairs; const newField = isShortDots ? shortenDottedString(key) : key; - const val = formatted[key]; - pairs.push([newField, val]); + const val = formatted![key]; + pairs.push([newField as string, val]); }, []); return ReactDOM.renderToStaticMarkup( diff --git a/src/plugins/field_formats/common/converters/static_lookup.ts b/src/plugins/field_formats/common/converters/static_lookup.ts index 455f4794887f9..d52196d910b80 100644 --- a/src/plugins/field_formats/common/converters/static_lookup.ts +++ b/src/plugins/field_formats/common/converters/static_lookup.ts @@ -11,13 +11,15 @@ import { KBN_FIELD_TYPES } from '@kbn/field-types'; import { FieldFormat } from '../field_format'; import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; -function convertLookupEntriesToMap(lookupEntries: any[]) { +function convertLookupEntriesToMap( + lookupEntries: Array<{ key: string; value: unknown }> +): Record { return lookupEntries.reduce( - (lookupMap: Record, lookupEntry: Record) => { + (lookupMap: Record, lookupEntry: { key: string; value: unknown }) => { lookupMap[lookupEntry.key] = lookupEntry.value; return lookupMap; }, - {} + {} as Record ); } @@ -41,7 +43,7 @@ export class StaticLookupFormat extends FieldFormat { }; } - textConvert: TextContextTypeConvert = (val) => { + textConvert: TextContextTypeConvert = (val: string) => { const lookupEntries = this.param('lookupEntries'); const unknownKeyValue = this.param('unknownKeyValue'); diff --git a/src/plugins/field_formats/common/converters/string.ts b/src/plugins/field_formats/common/converters/string.ts index a3d571897ef61..e3700e1b52429 100644 --- a/src/plugins/field_formats/common/converters/string.ts +++ b/src/plugins/field_formats/common/converters/string.ts @@ -109,7 +109,7 @@ export class StringFormat extends FieldFormat { }); } - textConvert: TextContextTypeConvert = (val) => { + textConvert: TextContextTypeConvert = (val: string | number) => { if (val === '') { return emptyLabel; } @@ -119,13 +119,13 @@ export class StringFormat extends FieldFormat { case 'upper': return String(val).toUpperCase(); case 'title': - return this.toTitleCase(val); + return this.toTitleCase(String(val)); case 'short': - return shortenDottedString(val); + return asPrettyString(shortenDottedString(val)); case 'base64': - return this.base64Decode(val); + return this.base64Decode(String(val)); case 'urlparam': - return decodeURIComponent(val); + return decodeURIComponent(String(val)); default: return asPrettyString(val); } @@ -136,8 +136,8 @@ export class StringFormat extends FieldFormat { return `${emptyLabel}`; } - return hit?.highlight?.[field?.name] - ? getHighlightHtml(escape(val), hit.highlight[field.name]) + return hit?.highlight?.[field?.name!] + ? getHighlightHtml(escape(val), hit.highlight[field!.name]) : escape(this.textConvert(val)); }; } diff --git a/src/plugins/field_formats/common/converters/truncate.ts b/src/plugins/field_formats/common/converters/truncate.ts index 2ac4b515325eb..bea55f5120f04 100644 --- a/src/plugins/field_formats/common/converters/truncate.ts +++ b/src/plugins/field_formats/common/converters/truncate.ts @@ -22,7 +22,7 @@ export class TruncateFormat extends FieldFormat { }); static fieldType = KBN_FIELD_TYPES.STRING; - textConvert: TextContextTypeConvert = (val) => { + textConvert: TextContextTypeConvert = (val: string) => { const length = this.param('fieldLength'); if (length > 0) { return truncate(val, { diff --git a/src/plugins/field_formats/common/converters/url.ts b/src/plugins/field_formats/common/converters/url.ts index 930c94f9ce0fa..f788db29823f7 100644 --- a/src/plugins/field_formats/common/converters/url.ts +++ b/src/plugins/field_formats/common/converters/url.ts @@ -14,8 +14,9 @@ import { FieldFormat } from '../field_format'; import { TextContextTypeConvert, HtmlContextTypeConvert, - IFieldFormatMetaParams, + FieldFormatMetaParams, FIELD_FORMAT_IDS, + FieldFormatParams, } from '../types'; const templateMatchRE = /{{([\s\S]+?)}}/g; @@ -61,7 +62,7 @@ export class UrlFormat extends FieldFormat { ]; static urlTypes = URL_TYPES; - constructor(params: IFieldFormatMetaParams) { + constructor(params: FieldFormatParams & FieldFormatMetaParams) { super(params); this.compileTemplate = memoize(this.compileTemplate); } @@ -101,7 +102,7 @@ export class UrlFormat extends FieldFormat { // trim all the odd bits, the variable names const parts = template.split(templateMatchRE).map((part, i) => (i % 2 ? part.trim() : part)); - return function (locals: Record): string { + return function (locals: Record): string { // replace all the odd bits with their local var let output = ''; let i = -1; @@ -129,9 +130,9 @@ export class UrlFormat extends FieldFormat { return `${imageLabel}`; } - textConvert: TextContextTypeConvert = (value) => this.formatLabel(value); + textConvert: TextContextTypeConvert = (value: string) => this.formatLabel(value); - htmlConvert: HtmlContextTypeConvert = (rawValue, options = {}) => { + htmlConvert: HtmlContextTypeConvert = (rawValue: string, options = {}) => { const { field, hit } = options; const { parsedUrl } = this._params; const { basePath, pathname, origin } = parsedUrl || {}; @@ -187,8 +188,8 @@ export class UrlFormat extends FieldFormat { let linkLabel; - if (hit && hit.highlight && hit.highlight[field.name]) { - linkLabel = getHighlightHtml(label, hit.highlight[field.name]); + if (hit && hit.highlight && hit.highlight[field?.name!]) { + linkLabel = getHighlightHtml(label, hit.highlight[field!.name]); } else { linkLabel = label; } diff --git a/src/plugins/field_formats/common/field_format.test.ts b/src/plugins/field_formats/common/field_format.test.ts index b69040c7c42db..4624c5ebe5e55 100644 --- a/src/plugins/field_formats/common/field_format.test.ts +++ b/src/plugins/field_formats/common/field_format.test.ts @@ -9,11 +9,12 @@ import { constant, trimEnd, trimStart, get } from 'lodash'; import { FieldFormat } from './field_format'; import { asPrettyString } from './utils'; +import { FieldFormatParams } from './types'; const getTestFormat = ( - _params?: Record, + _params?: FieldFormatParams, textConvert = (val: string) => asPrettyString(val), - htmlConvert?: any + htmlConvert?: (val: string) => string ) => new (class TestFormat extends FieldFormat { static id = 'test-format'; diff --git a/src/plugins/field_formats/common/field_format.ts b/src/plugins/field_formats/common/field_format.ts index 97b8acd7c019d..05b376ddf3272 100644 --- a/src/plugins/field_formats/common/field_format.ts +++ b/src/plugins/field_formats/common/field_format.ts @@ -16,7 +16,8 @@ import { FieldFormatConvertFunction, HtmlContextTypeOptions, TextContextTypeOptions, - IFieldFormatMetaParams, + FieldFormatMetaParams, + FieldFormatParams, } from './types'; import { htmlContentTypeSetup, textContentTypeSetup, TEXT_CONTEXT_TYPE } from './content_types'; import { HtmlContextTypeConvert, TextContextTypeConvert } from './types'; @@ -83,13 +84,16 @@ export abstract class FieldFormat { * @property {Function} - ref to child class * @private */ - public type: any = this.constructor; + public type = this.constructor as typeof FieldFormat; public allowsNumericalAggregations?: boolean; - protected readonly _params: any; + protected readonly _params: FieldFormatParams & FieldFormatMetaParams; protected getConfig: FieldFormatsGetConfigFn | undefined; - constructor(_params: IFieldFormatMetaParams = {}, getConfig?: FieldFormatsGetConfigFn) { + constructor( + _params: FieldFormatParams & FieldFormatMetaParams = {}, + getConfig?: FieldFormatsGetConfigFn + ) { this._params = _params; if (getConfig) { @@ -99,7 +103,7 @@ export abstract class FieldFormat { /** * Convert a raw value to a formatted string - * @param {any} value + * @param {unknown} value * @param {string} [contentType=text] - optional content type, the only two contentTypes * currently supported are "html" and "text", which helps * formatters adjust to different contexts @@ -108,7 +112,7 @@ export abstract class FieldFormat { * @public */ convert( - value: any, + value: unknown, contentType: FieldFormatsContentType = DEFAULT_CONTEXT_TYPE, options?: HtmlContextTypeOptions | TextContextTypeOptions ): string { @@ -118,7 +122,8 @@ export abstract class FieldFormat { return converter.call(this, value, options); } - return value; + // TODO: should be "return `${value}`;", but might be a breaking change + return value as string; } /** @@ -142,7 +147,7 @@ export abstract class FieldFormat { * @return {object} - parameter defaults * @public */ - getParamDefaults(): Record { + getParamDefaults(): FieldFormatParams { return {}; } @@ -150,7 +155,7 @@ export abstract class FieldFormat { * Get the value of a param. This value may be a default value. * * @param {string} name - the param name to fetch - * @return {any} + * @return {any} TODO: https://github.com/elastic/kibana/issues/108158 * @public */ param(name: string): any { @@ -170,7 +175,7 @@ export abstract class FieldFormat { * @return {object} * @public */ - params(): Record { + params(): FieldFormatParams & FieldFormatMetaParams { return cloneDeep(defaults({}, this._params, this.getParamDefaults())); } @@ -182,12 +187,12 @@ export abstract class FieldFormat { * @public */ toJSON() { - const id = get(this.type, 'id'); + const id = this.type.id; const defaultsParams = this.getParamDefaults() || {}; const params = transform( this._params, - (uniqParams: any, val, param: string) => { + (uniqParams: FieldFormatParams & FieldFormatMetaParams, val: unknown, param: string) => { if (param === 'parsedUrl') return; if (param && val !== get(defaultsParams, param)) { uniqParams[param] = val; @@ -198,7 +203,7 @@ export abstract class FieldFormat { return { id, - params: size(params) ? (params as any) : undefined, + params: size(params) ? params : undefined, }; } @@ -213,7 +218,7 @@ export abstract class FieldFormat { }; } - static isInstanceOfFieldFormat(fieldFormat: any): fieldFormat is FieldFormat { - return Boolean(fieldFormat && fieldFormat.convert); + static isInstanceOfFieldFormat(fieldFormat: unknown): fieldFormat is FieldFormat { + return Boolean(fieldFormat && typeof fieldFormat === 'object' && 'convert' in fieldFormat); } } diff --git a/src/plugins/field_formats/common/field_formats_registry.test.ts b/src/plugins/field_formats/common/field_formats_registry.test.ts index e94efc88be20f..ce5cb2eec8f07 100644 --- a/src/plugins/field_formats/common/field_formats_registry.test.ts +++ b/src/plugins/field_formats/common/field_formats_registry.test.ts @@ -8,14 +8,12 @@ import { FieldFormatsRegistry } from './field_formats_registry'; import { BoolFormat, PercentFormat, StringFormat } from './converters'; -import { FieldFormatsGetConfigFn, FieldFormatInstanceType } from './types'; +import { FieldFormatConfig, FieldFormatsGetConfigFn } from './types'; import { KBN_FIELD_TYPES } from '@kbn/field-types'; -const getValueOfPrivateField = (instance: any, field: string) => instance[field]; - describe('FieldFormatsRegistry', () => { let fieldFormatsRegistry: FieldFormatsRegistry; - let defaultMap = {}; + let defaultMap: Record = ({} = {}); const getConfig = (() => defaultMap) as FieldFormatsGetConfigFn; beforeEach(() => { @@ -35,9 +33,6 @@ describe('FieldFormatsRegistry', () => { test('should allows to create an instance of "FieldFormatsRegistry"', () => { expect(fieldFormatsRegistry).toBeDefined(); - - expect(getValueOfPrivateField(fieldFormatsRegistry, 'fieldFormats')).toBeDefined(); - expect(getValueOfPrivateField(fieldFormatsRegistry, 'defaultMap')).toEqual({}); }); describe('init', () => { @@ -48,11 +43,13 @@ describe('FieldFormatsRegistry', () => { test('should populate the "defaultMap" object', () => { defaultMap = { - number: { id: 'number', params: {} }, + [KBN_FIELD_TYPES.NUMBER]: { id: KBN_FIELD_TYPES.NUMBER, params: {} }, }; fieldFormatsRegistry.init(getConfig, {}, []); - expect(getValueOfPrivateField(fieldFormatsRegistry, 'defaultMap')).toEqual(defaultMap); + expect(fieldFormatsRegistry.getDefaultConfig(KBN_FIELD_TYPES.NUMBER)).toEqual( + defaultMap[KBN_FIELD_TYPES.NUMBER] + ); }); }); @@ -65,16 +62,9 @@ describe('FieldFormatsRegistry', () => { test('should register field formats', () => { fieldFormatsRegistry.register([StringFormat, BoolFormat]); - const registeredFieldFormatters: Map< - string, - FieldFormatInstanceType - > = getValueOfPrivateField(fieldFormatsRegistry, 'fieldFormats'); - - expect(registeredFieldFormatters.size).toBe(2); - - expect(registeredFieldFormatters.get(BoolFormat.id)).toBe(BoolFormat); - expect(registeredFieldFormatters.get(StringFormat.id)).toBe(StringFormat); - expect(registeredFieldFormatters.get(PercentFormat.id)).toBeUndefined(); + expect(fieldFormatsRegistry.has(StringFormat.id)).toBe(true); + expect(fieldFormatsRegistry.has(BoolFormat.id)).toBe(true); + expect(fieldFormatsRegistry.has(PercentFormat.id)).toBe(false); }); test('should throw if registering a formatter with existing id ', () => { @@ -130,8 +120,8 @@ describe('FieldFormatsRegistry', () => { const stringFormat = new DecoratedStingFormat({ foo: 'foo', }); - const params = getValueOfPrivateField(stringFormat, '_params'); + const params = stringFormat.params(); expect(params).toHaveProperty('foo'); expect(params).toHaveProperty('parsedUrl'); expect(params.parsedUrl).toHaveProperty('origin'); @@ -168,7 +158,7 @@ describe('FieldFormatsRegistry', () => { expect(DecoratedStringFormat).toBeDefined(); const stingFormat = new DecoratedStringFormat({ foo: 'foo' }); - const params = getValueOfPrivateField(stingFormat, '_params'); + const params = stingFormat.params(); expect(params).toHaveProperty('foo'); expect(params).toHaveProperty('parsedUrl'); diff --git a/src/plugins/field_formats/common/field_formats_registry.ts b/src/plugins/field_formats/common/field_formats_registry.ts index 675ec897c2b70..3d856e4686126 100644 --- a/src/plugins/field_formats/common/field_formats_registry.ts +++ b/src/plugins/field_formats/common/field_formats_registry.ts @@ -7,7 +7,7 @@ */ // eslint-disable-next-line max-classes-per-file -import { forOwn, isFunction, memoize, identity } from 'lodash'; +import { memoize, identity } from 'lodash'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types'; import { @@ -16,9 +16,10 @@ import { FIELD_FORMAT_IDS, FieldFormatInstanceType, FieldFormatId, - IFieldFormatMetaParams, + FieldFormatMetaParams, SerializedFieldFormat, FormatFactory, + FieldFormatParams, } from './types'; import { baseFormatters } from './constants/base_formatters'; import { FieldFormat } from './field_format'; @@ -28,7 +29,7 @@ import { FieldFormatNotFoundError } from './errors'; export class FieldFormatsRegistry { protected fieldFormats: Map = new Map(); protected defaultMap: Record = {}; - protected metaParamsOptions: Record = {}; + protected metaParamsOptions: FieldFormatMetaParams = {}; protected getConfig?: FieldFormatsGetConfigFn; public deserialize: FormatFactory = (mapping?: SerializedFieldFormat) => { @@ -50,10 +51,13 @@ export class FieldFormatsRegistry { init( getConfig: FieldFormatsGetConfigFn, - metaParamsOptions: Record = {}, + metaParamsOptions: FieldFormatMetaParams = {}, defaultFieldConverters: FieldFormatInstanceType[] = baseFormatters ) { - const defaultTypeMap = getConfig(FORMATS_UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP); + const defaultTypeMap = getConfig(FORMATS_UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP) as Record< + string, + FieldFormatConfig + >; this.register(defaultFieldConverters); this.parseDefaultTypeMap(defaultTypeMap); this.getConfig = getConfig; @@ -89,10 +93,10 @@ export class FieldFormatsRegistry { const fieldFormat = this.fieldFormats.get(formatId); if (fieldFormat) { - const decoratedFieldFormat: any = this.fieldFormatMetaParamsDecorator(fieldFormat); + const decoratedFieldFormat = this.fieldFormatMetaParamsDecorator(fieldFormat); if (decoratedFieldFormat) { - return decoratedFieldFormat as FieldFormatInstanceType; + return decoratedFieldFormat; } } @@ -159,8 +163,12 @@ export class FieldFormatsRegistry { * @param {FieldFormatId} formatId * @return {FieldFormat} */ - getInstance = memoize( - (formatId: FieldFormatId, params: Record = {}): FieldFormat => { + getInstance = (formatId: FieldFormatId, params: FieldFormatParams = {}): FieldFormat => { + return this.getInstanceMemoized(formatId, params); + }; + + private getInstanceMemoized = memoize( + (formatId: FieldFormatId, params: FieldFormatParams = {}): FieldFormat => { const ConcreteFieldFormat = this.getType(formatId); if (!ConcreteFieldFormat) { @@ -169,7 +177,7 @@ export class FieldFormatsRegistry { return new ConcreteFieldFormat(params, this.getConfig); }, - (formatId: FieldFormatId, params: Record) => + (formatId: FieldFormatId, params: FieldFormatParams) => JSON.stringify({ formatId, ...params, @@ -186,7 +194,7 @@ export class FieldFormatsRegistry { getDefaultInstancePlain = ( fieldType: KBN_FIELD_TYPES, esTypes?: ES_FIELD_TYPES[], - params: Record = {} + params: FieldFormatParams = {} ): FieldFormat => { const conf = this.getDefaultConfig(fieldType, esTypes); const instanceParams = { @@ -239,19 +247,26 @@ export class FieldFormatsRegistry { * * @param {KBN_FIELD_TYPES} fieldType * @param {ES_FIELD_TYPES[]} esTypes + * @param {FieldFormatParams} params * @return {FieldFormat} */ - getDefaultInstance = memoize(this.getDefaultInstancePlain, this.getDefaultInstanceCacheResolver); + getDefaultInstance = ( + fieldType: KBN_FIELD_TYPES, + esTypes?: ES_FIELD_TYPES[], + params: FieldFormatParams = {} + ): FieldFormat => { + return this.getDefaultInstanceMemoized(fieldType, esTypes, params); + }; + + private getDefaultInstanceMemoized = memoize( + this.getDefaultInstancePlain, + this.getDefaultInstanceCacheResolver + ); - parseDefaultTypeMap(value: any) { + parseDefaultTypeMap(value: Record) { this.defaultMap = value; - forOwn(this, (fn) => { - if (isFunction(fn) && (fn as any).cache) { - // clear all memoize caches - // @ts-ignore - fn.cache = new memoize.Cache(); - } - }); + this.getInstanceMemoized.cache.clear?.(); + this.getDefaultInstanceMemoized.cache.clear?.(); } register(fieldFormats: FieldFormatInstanceType[]) { @@ -282,14 +297,14 @@ export class FieldFormatsRegistry { private fieldFormatMetaParamsDecorator = ( fieldFormat: FieldFormatInstanceType ): FieldFormatInstanceType | undefined => { - const getMetaParams = (customParams: Record) => this.buildMetaParams(customParams); + const getMetaParams = (customParams: FieldFormatParams) => this.buildMetaParams(customParams); if (fieldFormat) { return class DecoratedFieldFormat extends fieldFormat { static id = fieldFormat.id; static fieldType = fieldFormat.fieldType; - constructor(params: Record = {}, getConfig?: FieldFormatsGetConfigFn) { + constructor(params: FieldFormatParams = {}, getConfig?: FieldFormatsGetConfigFn) { super(getMetaParams(params), getConfig); } }; @@ -301,10 +316,12 @@ export class FieldFormatsRegistry { /** * Build Meta Params * - * @param {Record} custom params - * @return {Record} + * @param {FieldFormatParams} custom params + * @return {FieldFormatParams & FieldFormatMetaParams} */ - private buildMetaParams = (customParams: T): T => ({ + private buildMetaParams = ( + customParams: FieldFormatParams + ): FieldFormatParams & FieldFormatMetaParams => ({ ...this.metaParamsOptions, ...customParams, }); diff --git a/src/plugins/field_formats/common/index.ts b/src/plugins/field_formats/common/index.ts index 2e0aa82a67b30..aeb5e0af220db 100644 --- a/src/plugins/field_formats/common/index.ts +++ b/src/plugins/field_formats/common/index.ts @@ -46,8 +46,15 @@ export { FieldFormatId, SerializedFieldFormat, FormatFactory, - // Used in field format plugin only FieldFormatInstanceType, IFieldFormat, FieldFormatsStartCommon, + FieldFormatParams, + FieldFormatMetaParams, + FieldFormatConvert, + FieldFormatConvertFunction, + HtmlContextTypeConvert, + HtmlContextTypeOptions, + TextContextTypeConvert, + TextContextTypeOptions, } from './types'; diff --git a/src/plugins/field_formats/common/mocks.ts b/src/plugins/field_formats/common/mocks.ts index fa56afb2c277c..9d2038e18611a 100644 --- a/src/plugins/field_formats/common/mocks.ts +++ b/src/plugins/field_formats/common/mocks.ts @@ -15,12 +15,12 @@ export const fieldFormatsMock: IFieldFormatsRegistry = { getDefaultInstance: jest.fn().mockImplementation(() => ({ convert: jest.fn().mockImplementation((t: string) => t), getConverterFor: jest.fn().mockImplementation(() => (t: string) => t), - })) as any, + })), getDefaultInstanceCacheResolver: jest.fn(), getDefaultInstancePlain: jest.fn(), getDefaultType: jest.fn(), getDefaultTypeName: jest.fn(), - getInstance: jest.fn() as any, + getInstance: jest.fn(), getType: jest.fn(), getTypeNameByEsTypes: jest.fn(), init: jest.fn(), diff --git a/src/plugins/field_formats/common/types.ts b/src/plugins/field_formats/common/types.ts index c6048e1367a09..00f9f5d707e89 100644 --- a/src/plugins/field_formats/common/types.ts +++ b/src/plugins/field_formats/common/types.ts @@ -12,26 +12,44 @@ import { FieldFormatsRegistry } from './field_formats_registry'; /** @public **/ export type FieldFormatsContentType = 'html' | 'text'; -/** @internal **/ +/** + * Html converter options + */ export interface HtmlContextTypeOptions { - field?: any; - indexPattern?: any; - hit?: Record; + field?: { name: string }; + // TODO: get rid of indexPattern dep completely + indexPattern?: { + formatHit: (hit: { highlight: Record }) => Record; + }; + hit?: { highlight: Record }; } -/** @internal **/ +/** + * To html converter function + * @public + */ export type HtmlContextTypeConvert = (value: any, options?: HtmlContextTypeOptions) => string; -/** @internal **/ -export type TextContextTypeOptions = Record; +/** + * Plain text converter options + * @remark + * no options for now + */ +export type TextContextTypeOptions = object; -/** @internal **/ +/** + * To plain text converter function + * @public + */ export type TextContextTypeConvert = (value: any, options?: TextContextTypeOptions) => string; -/** @internal **/ +/** + * Converter function + * @public + */ export type FieldFormatConvertFunction = HtmlContextTypeConvert | TextContextTypeConvert; -/** @internal **/ +/** @public **/ export interface FieldFormatConvert { text: TextContextTypeConvert; html: HtmlContextTypeConvert; @@ -61,7 +79,7 @@ export enum FIELD_FORMAT_IDS { /** @public */ export interface FieldFormatConfig { id: FieldFormatId; - params: Record; + params: FieldFormatParams; es?: boolean; } @@ -74,10 +92,10 @@ export interface FieldFormatConfig { * This matches the signature of the public `core.uiSettings.get`, and * should only be used in scenarios where async access to uiSettings is * not possible. - * + * @public */ -export type FieldFormatsGetConfigFn = (key: string, defaultOverride?: T) => T; +export type FieldFormatsGetConfigFn = (key: string, defaultOverride?: T) => T; export type IFieldFormat = FieldFormat; @@ -86,9 +104,12 @@ export type IFieldFormat = FieldFormat; */ export type FieldFormatId = FIELD_FORMAT_IDS | string; -/** @internal **/ +/** + * Alternative to typeof {@link FieldFormat} but with specified ids + * @public + */ export type FieldFormatInstanceType = (new ( - params?: any, + params?: FieldFormatParams, getConfig?: FieldFormatsGetConfigFn ) => FieldFormat) & { // Static properties: @@ -98,8 +119,23 @@ export type FieldFormatInstanceType = (new ( fieldType: string | string[]; }; -export interface IFieldFormatMetaParams { - [key: string]: any; +/** + * Params provided when creating a formatter. + * Params are vary per formatter + * + * TODO: support strict typing for params depending on format type + * https://github.com/elastic/kibana/issues/108158 + */ +export interface FieldFormatParams { + [param: string]: any; +} + +/** + * Params provided by the registry to every field formatter + * + * @public + */ +export interface FieldFormatMetaParams { parsedUrl?: { origin: string; pathname?: string; @@ -116,7 +152,7 @@ export type FieldFormatsStartCommon = Omit> { +export interface SerializedFieldFormat { id?: string; params?: TParams; } diff --git a/src/plugins/field_formats/common/utils/as_pretty_string.ts b/src/plugins/field_formats/common/utils/as_pretty_string.ts index 52cdbe61d6b41..95424f00b2c44 100644 --- a/src/plugins/field_formats/common/utils/as_pretty_string.ts +++ b/src/plugins/field_formats/common/utils/as_pretty_string.ts @@ -9,7 +9,7 @@ /** * Convert a value to a presentable string */ -export function asPrettyString(val: any): string { +export function asPrettyString(val: unknown): string { if (val === null || val === undefined) return ' - '; switch (typeof val) { case 'string': diff --git a/src/plugins/field_formats/common/utils/highlight/highlight_html.ts b/src/plugins/field_formats/common/utils/highlight/highlight_html.ts index 250e238307363..8803a346b7a9e 100644 --- a/src/plugins/field_formats/common/utils/highlight/highlight_html.ts +++ b/src/plugins/field_formats/common/utils/highlight/highlight_html.ts @@ -10,7 +10,10 @@ import _ from 'lodash'; import { highlightTags } from './highlight_tags'; import { htmlTags } from './html_tags'; -export function getHighlightHtml(fieldValue: any, highlights: any) { +export function getHighlightHtml( + fieldValue: string | object, + highlights: string[] | undefined | null +) { let highlightHtml = typeof fieldValue === 'object' ? JSON.stringify(fieldValue) : fieldValue; _.each(highlights, function (highlight) { diff --git a/src/plugins/field_formats/common/utils/highlight/highlight_request.test.ts b/src/plugins/field_formats/common/utils/highlight/highlight_request.test.ts index a2a754b9e31f6..a8c20467f1300 100644 --- a/src/plugins/field_formats/common/utils/highlight/highlight_request.test.ts +++ b/src/plugins/field_formats/common/utils/highlight/highlight_request.test.ts @@ -9,19 +9,12 @@ import { getHighlightRequest } from './highlight_request'; describe('getHighlightRequest', () => { - const queryStringQuery = { query_string: { query: 'foo' } }; - test('should be a function', () => { expect(getHighlightRequest).toBeInstanceOf(Function); }); - test('should not modify the original query', () => { - getHighlightRequest(queryStringQuery, true); - expect(queryStringQuery.query_string).not.toHaveProperty('highlight'); - }); - test('should return undefined if highlighting is turned off', () => { - const request = getHighlightRequest(queryStringQuery, false); + const request = getHighlightRequest(false); expect(request).toBe(undefined); }); }); diff --git a/src/plugins/field_formats/common/utils/highlight/highlight_request.ts b/src/plugins/field_formats/common/utils/highlight/highlight_request.ts index fb87c29d4cc2c..369d68aabdf39 100644 --- a/src/plugins/field_formats/common/utils/highlight/highlight_request.ts +++ b/src/plugins/field_formats/common/utils/highlight/highlight_request.ts @@ -10,7 +10,7 @@ import { highlightTags } from './highlight_tags'; const FRAGMENT_SIZE = Math.pow(2, 31) - 1; // Max allowed value for fragment_size (limit of a java int) -export function getHighlightRequest(query: any, shouldHighlight: boolean) { +export function getHighlightRequest(shouldHighlight: boolean) { if (!shouldHighlight) return; return { diff --git a/src/plugins/field_formats/common/utils/shorten_dotted_string.ts b/src/plugins/field_formats/common/utils/shorten_dotted_string.ts index 53f7471913dc3..31c0f609ac5a3 100644 --- a/src/plugins/field_formats/common/utils/shorten_dotted_string.ts +++ b/src/plugins/field_formats/common/utils/shorten_dotted_string.ts @@ -12,8 +12,8 @@ const DOT_PREFIX_RE = /(.).+?\./g; * Convert a dot.notated.string into a short * version (d.n.string) * - * @return {any} + * @return {unknown} */ -export function shortenDottedString(input: any) { +export function shortenDottedString(input: unknown) { return typeof input !== 'string' ? input : input.replace(DOT_PREFIX_RE, '$1.'); } diff --git a/src/plugins/field_formats/public/lib/converters/date.test.ts b/src/plugins/field_formats/public/lib/converters/date.test.ts index f4279fe5f9a7c..a9152b201b1a4 100644 --- a/src/plugins/field_formats/public/lib/converters/date.test.ts +++ b/src/plugins/field_formats/public/lib/converters/date.test.ts @@ -8,17 +8,23 @@ import moment from 'moment-timezone'; import { DateFormat } from './date'; +import { FieldFormatsGetConfigFn } from '../../../common'; describe('Date Format', () => { let convert: Function; - let mockConfig: Record; + let mockConfig: { + dateFormat: string; + 'dateFormat:tz': string; + [other: string]: string; + }; beforeEach(() => { - mockConfig = {}; - mockConfig.dateFormat = 'MMMM Do YYYY, HH:mm:ss.SSS'; - mockConfig['dateFormat:tz'] = 'Browser'; + mockConfig = { + dateFormat: 'MMMM Do YYYY, HH:mm:ss.SSS', + 'dateFormat:tz': 'Browser', + }; - const getConfig = (key: string) => mockConfig[key]; + const getConfig: FieldFormatsGetConfigFn = (key: string) => mockConfig[key]; const date = new DateFormat({}, getConfig); diff --git a/src/plugins/field_formats/public/lib/converters/date.ts b/src/plugins/field_formats/public/lib/converters/date.ts index acc051afd6b1d..1b0d9354fc04a 100644 --- a/src/plugins/field_formats/public/lib/converters/date.ts +++ b/src/plugins/field_formats/public/lib/converters/date.ts @@ -31,7 +31,7 @@ export class DateFormat extends FieldFormat { }; } - textConvert: TextContextTypeConvert = (val) => { + textConvert: TextContextTypeConvert = (val: string | number) => { // don't give away our ref to converter so // we can hot-swap when config changes const pattern = this.param('pattern'); @@ -43,7 +43,7 @@ export class DateFormat extends FieldFormat { this.timeZone = timezone; this.memoizedPattern = pattern; - this.memoizedConverter = memoize(function converter(value: any) { + this.memoizedConverter = memoize(function converter(value: string | number) { if (value === null || value === undefined) { return '-'; } diff --git a/src/plugins/field_formats/server/lib/converters/date_nanos_server.test.ts b/src/plugins/field_formats/server/lib/converters/date_nanos_server.test.ts index 86f0b2a7dc875..762ee172e921b 100644 --- a/src/plugins/field_formats/server/lib/converters/date_nanos_server.test.ts +++ b/src/plugins/field_formats/server/lib/converters/date_nanos_server.test.ts @@ -11,15 +11,20 @@ import { FieldFormatsGetConfigFn } from '../../../common'; describe('Date Nanos Format: Server side edition', () => { let convert: Function; - let mockConfig: Record; + let mockConfig: { + dateNanosFormat: string; + 'dateFormat:tz': string; + [other: string]: string; + }; let getConfig: FieldFormatsGetConfigFn; const dateTime = '2019-05-05T14:04:56.201900001Z'; beforeEach(() => { - mockConfig = {}; - mockConfig.dateNanosFormat = 'MMMM Do YYYY, HH:mm:ss.SSSSSSSSS'; - mockConfig['dateFormat:tz'] = 'Browser'; + mockConfig = { + dateNanosFormat: 'MMMM Do YYYY, HH:mm:ss.SSSSSSSSS', + 'dateFormat:tz': 'Browser', + }; getConfig = (key: string) => mockConfig[key]; }); diff --git a/src/plugins/field_formats/server/lib/converters/date_nanos_server.ts b/src/plugins/field_formats/server/lib/converters/date_nanos_server.ts index d47475f6274fa..b273ee6008cf3 100644 --- a/src/plugins/field_formats/server/lib/converters/date_nanos_server.ts +++ b/src/plugins/field_formats/server/lib/converters/date_nanos_server.ts @@ -16,7 +16,7 @@ import { import { TextContextTypeConvert } from '../../../common/types'; class DateNanosFormatServer extends DateNanosFormat { - textConvert: TextContextTypeConvert = (val) => { + textConvert: TextContextTypeConvert = (val: string | number) => { // don't give away our ref to converter so // we can hot-swap when config changes const pattern = this.param('pattern'); @@ -30,7 +30,7 @@ class DateNanosFormatServer extends DateNanosFormat { this.timeZone = timezone; this.memoizedPattern = pattern; - this.memoizedConverter = memoize((value: any) => { + this.memoizedConverter = memoize((value: string | number) => { if (value === null || value === undefined) { return '-'; } @@ -44,16 +44,16 @@ class DateNanosFormatServer extends DateNanosFormat { if (this.timeZone === 'Browser') { // Assume a warning has been logged that this can be unpredictable. It // would be too verbose to log anything here. - date = moment.utc(val); + date = moment.utc(value); } else { - date = moment.utc(val).tz(this.timeZone); + date = moment.utc(value).tz(this.timeZone); } if (typeof value !== 'string' && date.isValid()) { // fallback for max/min aggregation, where unixtime in ms is returned as a number // aggregations in Elasticsearch generally just return ms return date.format(fallbackPattern); - } else if (date.isValid()) { + } else if (date.isValid() && typeof value === 'string') { return formatWithNanos(date, value, fractPattern); } else { return value; diff --git a/src/plugins/field_formats/server/lib/converters/date_server.ts b/src/plugins/field_formats/server/lib/converters/date_server.ts index bf2151db0072c..56be3dcf3d360 100644 --- a/src/plugins/field_formats/server/lib/converters/date_server.ts +++ b/src/plugins/field_formats/server/lib/converters/date_server.ts @@ -11,7 +11,11 @@ import { memoize, noop } from 'lodash'; import moment from 'moment-timezone'; import { KBN_FIELD_TYPES } from '@kbn/field-types'; import { FieldFormat, FIELD_FORMAT_IDS, FieldFormatsGetConfigFn } from '../../../common'; -import { IFieldFormatMetaParams, TextContextTypeConvert } from '../../../common/types'; +import { + FieldFormatMetaParams, + FieldFormatParams, + TextContextTypeConvert, +} from '../../../common/types'; export class DateFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.DATE; @@ -24,10 +28,13 @@ export class DateFormat extends FieldFormat { private memoizedPattern: string = ''; private timeZone: string = ''; - constructor(params: IFieldFormatMetaParams, getConfig?: FieldFormatsGetConfigFn) { + constructor( + params?: FieldFormatParams & FieldFormatMetaParams, + getConfig?: FieldFormatsGetConfigFn + ) { super(params, getConfig); - this.memoizedConverter = memoize((val: any) => { + this.memoizedConverter = memoize((val: string | number) => { if (val == null) { return '-'; } @@ -61,7 +68,7 @@ export class DateFormat extends FieldFormat { }; } - textConvert: TextContextTypeConvert = (val) => { + textConvert: TextContextTypeConvert = (val: string | number) => { // don't give away our ref to converter so we can hot-swap when config changes const pattern = this.param('pattern'); const timezone = this.param('timezone'); diff --git a/src/plugins/field_formats/server/plugin.test.ts b/src/plugins/field_formats/server/plugin.test.ts index 266d724ce5e22..da598c69d027c 100644 --- a/src/plugins/field_formats/server/plugin.test.ts +++ b/src/plugins/field_formats/server/plugin.test.ts @@ -7,17 +7,20 @@ */ import { DateFormat } from './lib/converters/date_server'; -import { coreMock } from '../../../core/server/mocks'; +import { coreMock, httpServerMock } from '../../../core/server/mocks'; import { FieldFormatsPlugin } from './plugin'; describe('FieldFormats registry server plugin', () => { test('DateFormat is server version', async () => { const plugin = new FieldFormatsPlugin(coreMock.createPluginInitializerContext()); const pluginStart = await plugin.start(coreMock.createStart()); - const uiSettings = coreMock.createStart().uiSettings.asScopedToClient({} as any); + const soClient = coreMock + .createStart() + .savedObjects.getScopedClient(httpServerMock.createKibanaRequest()); + const uiSettings = coreMock.createStart().uiSettings.asScopedToClient(soClient); const fieldFormatsRegistry = await pluginStart.fieldFormatServiceFactory(uiSettings); - const DateFormatFromRegsitry = fieldFormatsRegistry.getTypeWithoutMetaParams('date'); + const DateFormatFromRegistry = fieldFormatsRegistry.getTypeWithoutMetaParams('date'); - expect(DateFormatFromRegsitry).toEqual(DateFormat); + expect(DateFormatFromRegistry).toEqual(DateFormat); }); }); diff --git a/src/plugins/field_formats/server/ui_settings.ts b/src/plugins/field_formats/server/ui_settings.ts index f24ddc60e1f97..9597cad5516d7 100644 --- a/src/plugins/field_formats/server/ui_settings.ts +++ b/src/plugins/field_formats/server/ui_settings.ts @@ -17,7 +17,7 @@ import { FORMATS_UI_SETTINGS } from '../common'; // default fallback in case the locale is not found. const numeralLanguageIds = [ 'en', - ...numeralLanguages.map((numeralLanguage: any) => { + ...numeralLanguages.map((numeralLanguage: { id: string }) => { return numeralLanguage.id; }), ]; @@ -201,7 +201,10 @@ export function getUiSettings(): Record> { type: 'select', options: numeralLanguageIds, optionLabels: Object.fromEntries( - numeralLanguages.map((language: Record) => [language.id, language.name]) + numeralLanguages.map((language: { id: string; name: string }) => [ + language.id, + language.name, + ]) ), description: i18n.translate('fieldFormats.advancedSettings.format.formattingLocaleText', { defaultMessage: `{numeralLanguageLink} locale`, diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.tsx index 24582e853d38f..78dcc0f058075 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.tsx @@ -105,7 +105,7 @@ export class DurationFormatEditor extends DefaultFormatEditor< { + options={(format.type as typeof DurationFormat).inputFormats.map((fmt: InputFormat) => { return { value: fmt.kind, text: fmt.text, @@ -129,12 +129,14 @@ export class DurationFormatEditor extends DefaultFormatEditor< { - return { - value: fmt.method, - text: fmt.text, - }; - })} + options={(format.type as typeof DurationFormat).outputFormats.map( + (fmt: OutputFormat) => { + return { + value: fmt.method, + text: fmt.text, + }; + } + )} onChange={(e) => { this.onChange({ outputFormat: e.target.value }); }} diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/string/string.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/string/string.tsx index c5d53a0ce9c95..586700042a2ee 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/string/string.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/string/string.tsx @@ -15,16 +15,12 @@ import { DefaultFormatEditor, defaultState } from '../default/default'; import { FormatEditorSamples } from '../../samples'; import { formatId } from './constants'; +import { StringFormat } from '../../../../../../field_formats/common'; interface StringFormatEditorFormatParams { transform: string; } -interface TransformOptions { - kind: string; - text: string; -} - export class StringFormatEditor extends DefaultFormatEditor { static formatId = formatId; state = { @@ -40,7 +36,7 @@ export class StringFormatEditor extends DefaultFormatEditor { + options={((format.type as typeof StringFormat).transformOptions || []).map((option) => { return { - value: option.kind, + value: option.kind as string, text: option.text, }; })} diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/url.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/url.tsx index b5627a16732d8..4690e2ca453a1 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/url.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/url.tsx @@ -25,6 +25,7 @@ import { formatId } from './constants'; import { context as contextType } from '../../../../../../kibana_react/public'; import { FormatEditorProps } from '../types'; +import { UrlFormat } from '../../../../../../field_formats/common'; interface OnChangeParam { type: string; @@ -144,7 +145,7 @@ export class UrlFormatEditor extends DefaultFormatEditor< }; render() { - const { format, formatParams } = this.props; + const { formatParams, format } = this.props; const { error, samples, sampleConverterType } = this.state; return ( @@ -157,7 +158,7 @@ export class UrlFormatEditor extends DefaultFormatEditor< { + options={(format.type as typeof UrlFormat).urlTypes.map((type: UrlType) => { return { value: type.kind, text: type.text, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/types.ts b/x-pack/plugins/lens/public/indexpattern_datasource/types.ts index 72acc114ca4b2..588b259520272 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/types.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/types.ts @@ -9,6 +9,7 @@ import type { IndexPatternColumn, IncompleteColumn } from './operations'; import type { IndexPatternAggRestrictions } from '../../../../../src/plugins/data/public'; import type { FieldSpec } from '../../../../../src/plugins/data/common'; import type { DragDropIdentifier } from '../drag_drop/providers'; +import type { FieldFormatParams } from '../../../../../src/plugins/field_formats/common'; export { FieldBasedIndexPatternColumn, @@ -51,7 +52,7 @@ export interface IndexPattern { string, { id: string; - params: unknown; + params: FieldFormatParams; } >; hasRestrictions: boolean; From b7b593a7071963ca330fc06dcd12eb90228f95d6 Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Mon, 13 Sep 2021 16:22:38 +0100 Subject: [PATCH 5/6] [Security Solution] Timeline uses existing filter manager (#111732) * use existing filterManager * remove unused default value * unit test * fix type Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../hover_actions/use_hover_action_items.tsx | 13 ++++++++----- .../timeline/query_tab_content/index.tsx | 17 ++++++++++++++--- .../public/timelines/store/timeline/helpers.ts | 1 + .../timelines/store/timeline/reducer.test.ts | 18 ++++++++++++++++++ 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_action_items.tsx b/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_action_items.tsx index f717a72ab8ad5..7d480c9e4b04f 100644 --- a/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_action_items.tsx +++ b/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_action_items.tsx @@ -20,6 +20,7 @@ import { SourcererScopeName } from '../../store/sourcerer/model'; import { useSourcererScope } from '../../containers/sourcerer'; import { timelineSelectors } from '../../../timelines/store/timeline'; import { ShowTopNButton } from './actions/show_top_n'; +import { FilterManager } from '../../../../../../../src/plugins/data/public'; export interface UseHoverActionItemsProps { dataProvider?: DataProvider | DataProvider[]; @@ -74,7 +75,7 @@ export const useHoverActionItems = ({ values, }: UseHoverActionItemsProps): UseHoverActionItems => { const kibana = useKibana(); - const { timelines } = kibana.services; + const { timelines, uiSettings } = kibana.services; // Common actions used by the alert table and alert flyout const { getAddToTimelineButton, @@ -84,17 +85,19 @@ export const useHoverActionItems = ({ getFilterOutValueButton, getOverflowButton, } = timelines.getHoverActions(); - const filterManagerBackup = useMemo(() => kibana.services.data.query.filterManager, [ kibana.services.data.query.filterManager, ]); const getManageTimeline = useMemo(() => timelineSelectors.getManageTimelineById(), []); - const { filterManager: activeFilterMananager } = useDeepEqualSelector((state) => + const { filterManager: activeFilterManager } = useDeepEqualSelector((state) => getManageTimeline(state, timelineId ?? '') ); const filterManager = useMemo( - () => (timelineId === TimelineId.active ? activeFilterMananager : filterManagerBackup), - [timelineId, activeFilterMananager, filterManagerBackup] + () => + timelineId === TimelineId.active + ? activeFilterManager ?? new FilterManager(uiSettings) + : filterManagerBackup, + [uiSettings, timelineId, activeFilterManager, filterManagerBackup] ); // Regarding data from useManageTimeline: diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx index abbb991c274da..3dc2d98f16462 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx @@ -14,7 +14,7 @@ import { EuiBadge, } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; -import React, { useState, useMemo, useEffect, useCallback } from 'react'; +import React, { useMemo, useEffect, useCallback } from 'react'; import styled from 'styled-components'; import { Dispatch } from 'redux'; import { connect, ConnectedProps, useDispatch } from 'react-redux'; @@ -60,6 +60,7 @@ import { activeTimeline } from '../../../containers/active_timeline_context'; import { DetailsPanel } from '../../side_panel'; import { ExitFullScreen } from '../../../../common/components/exit_full_screen'; import { defaultControlColumn } from '../body/control_columns'; +import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; const TimelineHeaderContainer = styled.div` margin-top: 6px; @@ -193,7 +194,17 @@ export const QueryTabContentComponent: React.FC = ({ } = useSourcererScope(SourcererScopeName.timeline); const { uiSettings } = useKibana().services; - const [filterManager] = useState(new FilterManager(uiSettings)); + + const getManageTimeline = useMemo(() => timelineSelectors.getManageTimelineById(), []); + const { filterManager: activeFilterManager } = useDeepEqualSelector((state) => + getManageTimeline(state, timelineId ?? '') + ); + + const filterManager = useMemo(() => activeFilterManager ?? new FilterManager(uiSettings), [ + activeFilterManager, + uiSettings, + ]); + const esQueryConfig = useMemo(() => esQuery.getEsQueryConfig(uiSettings), [uiSettings]); const kqlQuery: { query: string; @@ -256,7 +267,7 @@ export const QueryTabContentComponent: React.FC = ({ id: timelineId, }) ); - }, [filterManager, timelineId, dispatch]); + }, [activeFilterManager, dispatch, filterManager, timelineId, uiSettings]); const [ isQueryLoading, diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts index 7c07410a2789a..6ee844958aeed 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts @@ -156,6 +156,7 @@ export const addTimelineToStore = ({ ...timelineById, [id]: { ...timeline, + filterManager: timelineById[id].filterManager, isLoading: timelineById[id].isLoading, initialized: timelineById[id].initialized, dateRange: diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts index c0dcba6920b60..eceafb9b56cdd 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts @@ -52,6 +52,7 @@ import { TimelineModel } from './model'; import { timelineDefaults } from './defaults'; import { TimelineById } from './types'; import { Direction } from '../../../../common/search_strategy'; +import { FilterManager } from '../../../../../../../src/plugins/data/public'; jest.mock('../../../common/components/url_state/normalize_time_range.ts'); jest.mock('../../../common/utils/default_date_settings', () => { @@ -63,6 +64,8 @@ jest.mock('../../../common/utils/default_date_settings', () => { }; }); +const mockFilterManager = {} as FilterManager; + const basicDataProvider: DataProvider = { and: [], id: '123', @@ -97,6 +100,7 @@ const basicTimeline: TimelineModel = { eventIdToNoteIds: {}, excludedRowRendererIds: [], expandedDetail: {}, + filterManager: mockFilterManager, highlightedDropAndProviderId: '', historyIds: [], id: 'foo', @@ -194,6 +198,20 @@ describe('Timeline', () => { }, }); }); + + test('should contain existing filterManager', () => { + const update = addTimelineToStore({ + id: 'foo', + timeline: { + ...basicTimeline, + status: TimelineStatus.immutable, + timelineType: TimelineType.template, + }, + timelineById: timelineByIdMock, + }); + + expect(update.foo.filterManager).toEqual(mockFilterManager); + }); }); describe('#addNewTimeline', () => { From c01ff06cd2b3dcddd5a896ae417c218da78ac516 Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Mon, 13 Sep 2021 17:23:28 +0200 Subject: [PATCH 6/6] [ML] Functional tests - stabilize import job test (#111929) This PR stabilizes the import job tests by giving the `Import` button some time to be enabled before clicking it. --- .../services/ml/stack_management_jobs.ts | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/x-pack/test/functional/services/ml/stack_management_jobs.ts b/x-pack/test/functional/services/ml/stack_management_jobs.ts index 45b9fa2f29ccd..8be01f452219c 100644 --- a/x-pack/test/functional/services/ml/stack_management_jobs.ts +++ b/x-pack/test/functional/services/ml/stack_management_jobs.ts @@ -86,7 +86,7 @@ export function MachineLearningStackManagementJobsProvider( }, async executeSync() { - await testSubjects.click('mlJobMgmtSyncFlyoutSyncButton', 2000); + await testSubjects.clickWhenNotDisabled('mlJobMgmtSyncFlyoutSyncButton', { timeout: 5000 }); // check and close success toast const resultToast = await toasts.getToastElement(1); @@ -285,7 +285,18 @@ export function MachineLearningStackManagementJobsProvider( }, async importJobs() { - await testSubjects.click('mlJobMgmtImportImportButton', 1000); + await testSubjects.clickWhenNotDisabled('mlJobMgmtImportImportButton', { timeout: 5000 }); + + // check and close success toast + const resultToast = await toasts.getToastElement(1); + const titleElement = await testSubjects.findDescendant('euiToastHeader', resultToast); + const title: string = await titleElement.getVisibleText(); + expect(title).to.match(/^\d+ job[s]? successfully imported$/); + + const dismissButton = await testSubjects.findDescendant('toastCloseButton', resultToast); + await dismissButton.click(); + + // check that the flyout is closed await testSubjects.missingOrFail('mlJobMgmtImportJobsFlyout', { timeout: 60 * 1000 }); }, @@ -340,7 +351,18 @@ export function MachineLearningStackManagementJobsProvider( }, async selectExportJobs() { - await testSubjects.click('mlJobMgmtExportExportButton'); + await testSubjects.clickWhenNotDisabled('mlJobMgmtExportExportButton', { timeout: 5000 }); + + // check and close success toast + const resultToast = await toasts.getToastElement(1); + const titleElement = await testSubjects.findDescendant('euiToastHeader', resultToast); + const title: string = await titleElement.getVisibleText(); + expect(title).to.match(/^Your file is downloading in the background$/); + + const dismissButton = await testSubjects.findDescendant('toastCloseButton', resultToast); + await dismissButton.click(); + + // check that the flyout is closed await testSubjects.missingOrFail('mlJobMgmtExportJobsFlyout', { timeout: 60 * 1000 }); },