From edbad5375dd8d947781a048c0a39a6b768acca66 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Mon, 23 Dec 2019 18:56:59 +0300 Subject: [PATCH] Display changed field formats without requiring hard page refresh. (#53746) * Display changed field formats without requiring hard page refresh. * Update src/plugins/data/public/field_formats_provider/field_formats.ts Co-Authored-By: Vadim Dalecky * fix PR commetns Co-authored-by: Elastic Machine Co-authored-by: Vadim Dalecky --- .../field_formats/converters/date_server.ts | 4 +- .../common/field_formats/converters/url.ts | 4 +- .../data/common/field_formats/field_format.ts | 11 ++- .../data/common/field_formats/index.ts | 7 +- .../field_formats.test.ts | 45 ++++++++-- .../field_formats_provider/field_formats.ts | 84 ++++++++++++------- 6 files changed, 116 insertions(+), 39 deletions(-) diff --git a/src/plugins/data/common/field_formats/converters/date_server.ts b/src/plugins/data/common/field_formats/converters/date_server.ts index 0c214e424f163..34278ea9fe641 100644 --- a/src/plugins/data/common/field_formats/converters/date_server.ts +++ b/src/plugins/data/common/field_formats/converters/date_server.ts @@ -20,7 +20,7 @@ import { memoize, noop } from 'lodash'; import moment from 'moment-timezone'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; -import { FieldFormat } from '../field_format'; +import { FieldFormat, IFieldFormatMetaParams } from '../field_format'; import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; export class DateFormat extends FieldFormat { @@ -32,7 +32,7 @@ export class DateFormat extends FieldFormat { private memoizedPattern: string = ''; private timeZone: string = ''; - constructor(params: Record, getConfig: Function) { + constructor(params: IFieldFormatMetaParams, getConfig: Function) { super(params, getConfig); this.memoizedConverter = memoize((val: any) => { diff --git a/src/plugins/data/common/field_formats/converters/url.ts b/src/plugins/data/common/field_formats/converters/url.ts index ff9c0d724efc5..4b165986605a5 100644 --- a/src/plugins/data/common/field_formats/converters/url.ts +++ b/src/plugins/data/common/field_formats/converters/url.ts @@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n'; import { escape, memoize } from 'lodash'; import { getHighlightHtml } from '../utils'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; -import { FieldFormat } from '../field_format'; +import { FieldFormat, IFieldFormatMetaParams } from '../field_format'; import { TextContextTypeConvert, HtmlContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; const templateMatchRE = /{{([\s\S]+?)}}/g; @@ -64,7 +64,7 @@ export class UrlFormat extends FieldFormat { ]; static urlTypes = URL_TYPES; - constructor(params: Record) { + constructor(params: IFieldFormatMetaParams) { super(params); this.compileTemplate = memoize(this.compileTemplate); } diff --git a/src/plugins/data/common/field_formats/field_format.ts b/src/plugins/data/common/field_formats/field_format.ts index 50f07846a3ceb..a0621b27f5bc0 100644 --- a/src/plugins/data/common/field_formats/field_format.ts +++ b/src/plugins/data/common/field_formats/field_format.ts @@ -37,6 +37,15 @@ import { HtmlContextTypeConvert, TextContextTypeConvert } from './types'; const DEFAULT_CONTEXT_TYPE = TEXT_CONTEXT_TYPE; +export interface IFieldFormatMetaParams { + [key: string]: any; + parsedUrl?: { + origin: string; + pathname?: string; + basePath?: string; + }; +} + export abstract class FieldFormat { /** * @property {string} - Field Format Id @@ -90,7 +99,7 @@ export abstract class FieldFormat { protected readonly _params: any; protected getConfig: Function | undefined; - constructor(_params: Record = {}, getConfig?: Function) { + constructor(_params: IFieldFormatMetaParams = {}, getConfig?: Function) { this._params = _params; if (getConfig) { diff --git a/src/plugins/data/common/field_formats/index.ts b/src/plugins/data/common/field_formats/index.ts index b751b097b5ed2..b1e8744e26745 100644 --- a/src/plugins/data/common/field_formats/index.ts +++ b/src/plugins/data/common/field_formats/index.ts @@ -18,7 +18,12 @@ */ export { HTML_CONTEXT_TYPE, TEXT_CONTEXT_TYPE } from './content_types'; -export { FieldFormat, IFieldFormatType, IFieldFormatId } from './field_format'; +export { + FieldFormat, + IFieldFormatType, + IFieldFormatId, + IFieldFormatMetaParams, +} from './field_format'; export { getHighlightRequest, asPrettyString, getHighlightHtml } from './utils'; export * from './converters'; export * from './constants'; diff --git a/src/plugins/data/public/field_formats_provider/field_formats.test.ts b/src/plugins/data/public/field_formats_provider/field_formats.test.ts index e58435fc71418..8ad6cff77abc0 100644 --- a/src/plugins/data/public/field_formats_provider/field_formats.test.ts +++ b/src/plugins/data/public/field_formats_provider/field_formats.test.ts @@ -20,12 +20,13 @@ import { CoreSetup, IUiSettingsClient } from 'kibana/public'; import { FieldFormatRegisty } from './field_formats'; import { + BoolFormat, IFieldFormatType, PercentFormat, - BoolFormat, StringFormat, } from '../../common/field_formats'; import { coreMock } from '../../../../core/public/mocks'; +import { KBN_FIELD_TYPES } from '../../common'; const getValueOfPrivateField = (instance: any, field: string) => instance[field]; const getUiSettingsMock = (data: any): IUiSettingsClient['get'] => () => data; @@ -115,12 +116,12 @@ describe('FieldFormatRegisty', () => { test('should set meta params for all instances of FieldFormats', () => { fieldFormatRegisty.register([StringFormat]); - const ConcreteFormat = fieldFormatRegisty.getType(StringFormat.id); + const DecoratedStingFormat = fieldFormatRegisty.getType(StringFormat.id); - expect(ConcreteFormat).toBeDefined(); + expect(DecoratedStingFormat).toBeDefined(); - if (ConcreteFormat) { - const stringFormat = new ConcreteFormat({ + if (DecoratedStingFormat) { + const stringFormat = new DecoratedStingFormat({ foo: 'foo', }); const params = getValueOfPrivateField(stringFormat, '_params'); @@ -132,5 +133,39 @@ describe('FieldFormatRegisty', () => { expect(params.parsedUrl).toHaveProperty('basePath'); } }); + + test('should decorate static fields', () => { + fieldFormatRegisty.register([BoolFormat]); + + const DecoratedBoolFormat = fieldFormatRegisty.getType(BoolFormat.id); + + expect(DecoratedBoolFormat).toBeDefined(); + + if (DecoratedBoolFormat) { + expect(DecoratedBoolFormat.id).toBe(BoolFormat.id); + expect(DecoratedBoolFormat.fieldType).toBe(BoolFormat.fieldType); + } + }); + }); + + describe('getByFieldType', () => { + test('should provide an public "getByFieldType" method', () => { + expect(fieldFormatRegisty.getByFieldType).toBeDefined(); + expect(typeof fieldFormatRegisty.getByFieldType).toBe('function'); + }); + + test('should decorate returns types', () => { + fieldFormatRegisty.register([StringFormat, BoolFormat]); + + const [DecoratedStringFormat] = fieldFormatRegisty.getByFieldType(KBN_FIELD_TYPES.STRING); + + expect(DecoratedStringFormat).toBeDefined(); + + const stingFormat = new DecoratedStringFormat({ foo: 'foo' }); + const params = getValueOfPrivateField(stingFormat, '_params'); + + expect(params).toHaveProperty('foo'); + expect(params).toHaveProperty('parsedUrl'); + }); }); }); diff --git a/src/plugins/data/public/field_formats_provider/field_formats.ts b/src/plugins/data/public/field_formats_provider/field_formats.ts index 3d60965f2e532..d3d57d42028cd 100644 --- a/src/plugins/data/public/field_formats_provider/field_formats.ts +++ b/src/plugins/data/public/field_formats_provider/field_formats.ts @@ -17,6 +17,7 @@ * under the License. */ +// eslint-disable-next-line max-classes-per-file import { forOwn, isFunction, memoize } from 'lodash'; import { IUiSettingsClient, CoreSetup } from 'kibana/public'; import { @@ -26,6 +27,7 @@ import { IFieldFormatType, IFieldFormatId, FieldFormat, + IFieldFormatMetaParams, } from '../../common'; import { FieldType } from './types'; @@ -75,14 +77,20 @@ export class FieldFormatRegisty { * Get a derived FieldFormat class by its id. * * @param {IFieldFormatId} formatId - the format id - * @return {FieldFormat | void} + * @return {FieldFormat | undefined} */ - getType = (formatId: IFieldFormatId): IFieldFormatType | void => { - const decoratedFieldFormat: any = this.fieldFormatMetaParamsDecorator(formatId); + getType = (formatId: IFieldFormatId): IFieldFormatType | undefined => { + const fieldFormat = this.fieldFormats.get(formatId); - if (decoratedFieldFormat) { - return decoratedFieldFormat as IFieldFormatType; + if (fieldFormat) { + const decoratedFieldFormat: any = this.fieldFormatMetaParamsDecorator(fieldFormat); + + if (decoratedFieldFormat) { + return decoratedFieldFormat as IFieldFormatType; + } } + + return undefined; }; /** @@ -92,12 +100,12 @@ export class FieldFormatRegisty { * * @param {KBN_FIELD_TYPES} fieldType * @param {ES_FIELD_TYPES[]} esTypes - Array of ES data types - * @return {FieldFormat | void} + * @return {FieldFormat | undefined} */ getDefaultType = ( fieldType: KBN_FIELD_TYPES, esTypes: ES_FIELD_TYPES[] - ): IFieldFormatType | void => { + ): IFieldFormatType | undefined => { const config = this.getDefaultConfig(fieldType, esTypes); return this.getType(config.id); @@ -108,11 +116,11 @@ export class FieldFormatRegisty { * using the format:defaultTypeMap config map * * @param {ES_FIELD_TYPES[]} esTypes - Array of ES data types - * @return {ES_FIELD_TYPES | void} + * @return {ES_FIELD_TYPES | undefined} */ - getTypeNameByEsTypes = (esTypes: ES_FIELD_TYPES[] | undefined): ES_FIELD_TYPES | void => { + getTypeNameByEsTypes = (esTypes: ES_FIELD_TYPES[] | undefined): ES_FIELD_TYPES | undefined => { if (!Array.isArray(esTypes)) { - return; + return undefined; } return esTypes.find(type => this.defaultMap[type] && this.defaultMap[type].es); @@ -196,9 +204,12 @@ export class FieldFormatRegisty { * @return {FieldFormat[]} */ getByFieldType(fieldType: KBN_FIELD_TYPES): IFieldFormatType[] { - return [...this.fieldFormats.values()].filter( - (format: IFieldFormatType) => format.fieldType.indexOf(fieldType) !== -1 - ); + return [...this.fieldFormats.values()] + .filter((format: IFieldFormatType) => format && format.fieldType.indexOf(fieldType) !== -1) + .map( + (format: IFieldFormatType) => + this.fieldFormatMetaParamsDecorator(format) as IFieldFormatType + ); } /** @@ -232,24 +243,41 @@ export class FieldFormatRegisty { * FieldFormat decorator - provide a one way to add meta-params for all field formatters * * @private - * @param {IFieldFormatId} formatId - the format id - * @return {FieldFormat | void} + * @param {IFieldFormatType} fieldFormat - field format type + * @return {FieldFormat | undefined} */ - private fieldFormatMetaParamsDecorator = (formatId: IFieldFormatId): Function | void => { - const concreteFieldFormat = this.fieldFormats.get(formatId); - const decorateMetaParams = (customOptions: Record = {}) => ({ - parsedUrl: { - origin: window.location.origin, - pathname: window.location.pathname, - basePath: this.basePath, - }, - ...customOptions, - }); + private fieldFormatMetaParamsDecorator = ( + fieldFormat: IFieldFormatType + ): IFieldFormatType | undefined => { + const getMetaParams = (customParams: Record) => this.buildMetaParams(customParams); - if (concreteFieldFormat) { - return function(params: Record = {}, getConfig?: Function) { - return new concreteFieldFormat(decorateMetaParams(params), getConfig); + if (fieldFormat) { + return class DecoratedFieldFormat extends fieldFormat { + static id = fieldFormat.id; + static fieldType = fieldFormat.fieldType; + + constructor(params: Record = {}, getConfig?: Function) { + super(getMetaParams(params), getConfig); + } }; } + + return undefined; }; + + /** + * Build Meta Params + * + * @static + * @param {Record} custom params + * @return {Record} + */ + private buildMetaParams = (customParams: T): T => ({ + parsedUrl: { + origin: window.location.origin, + pathname: window.location.pathname, + basePath: this.basePath, + }, + ...customParams, + }); }