From 9388ff7b43d5743dfc8933690f1c67a7befef69d Mon Sep 17 00:00:00 2001 From: Joe Reuter <johannes.reuter@elastic.co> Date: Mon, 17 Feb 2020 13:52:53 +0100 Subject: [PATCH] Fix auto refresh in visualizations and lens (#57667) --- .../visualize/np_ready/editor/editor.js | 8 ----- .../public/embeddable/visualize_embeddable.ts | 8 +++++ .../visualize_embeddable_factory.tsx | 7 +++- .../public/np_ready/public/mocks.ts | 1 + .../public/np_ready/public/plugin.ts | 13 +++++-- .../timefilter/timefilter_service.mock.ts | 3 +- .../embeddable/embeddable.test.tsx | 35 ++++++++++++++++++- .../embeddable/embeddable.tsx | 15 +++++++- .../embeddable/embeddable_factory.ts | 8 ++++- .../public/editor_frame_service/mocks.tsx | 9 ++--- .../public/editor_frame_service/service.tsx | 1 + 11 files changed, 86 insertions(+), 22 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js index 27fb9b63843c4..657104344662f 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js @@ -441,14 +441,6 @@ function VisualizeAppController( }) ); - subscriptions.add( - subscribeWithScope($scope, timefilter.getAutoRefreshFetch$(), { - next: () => { - $scope.vis.forceReload(); - }, - }) - ); - $scope.$on('$destroy', () => { if ($scope._handler) { $scope._handler.destroy(); diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts index 5e593398333c9..fddcf70c30605 100644 --- a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -34,6 +34,7 @@ import { esFilters, Filter, ISearchSource, + TimefilterContract, } from '../../../../../plugins/data/public'; import { EmbeddableInput, @@ -106,8 +107,10 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut private vis: Vis; private domNode: any; public readonly type = VISUALIZE_EMBEDDABLE_TYPE; + private autoRefreshFetchSubscription: Subscription; constructor( + timefilter: TimefilterContract, { savedVisualization, editUrl, @@ -151,6 +154,10 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut this.vis._setUiState(this.uiState); + this.autoRefreshFetchSubscription = timefilter + .getAutoRefreshFetch$() + .subscribe(this.updateHandler.bind(this)); + this.subscriptions.push( Rx.merge(this.getOutput$(), this.getInput$()).subscribe(() => { this.handleChanges(); @@ -345,6 +352,7 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut this.handler.destroy(); this.handler.getElement().remove(); } + this.autoRefreshFetchSubscription.unsubscribe(); } public reload = () => { diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx index 03471174753fa..2f00467a85cda 100644 --- a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx +++ b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx @@ -38,6 +38,7 @@ import { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; import { getCapabilities, getHttp, getTypes, getUISettings } from '../np_ready/public/services'; import { showNewVisModal } from '../np_ready/public/wizard'; +import { TimefilterContract } from '../../../../../plugins/data/public'; interface VisualizationAttributes extends SavedObjectAttributes { visState: string; @@ -51,7 +52,10 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory< > { public readonly type = VISUALIZE_EMBEDDABLE_TYPE; - constructor(private getSavedVisualizationsLoader: () => SavedVisualizations) { + constructor( + private timefilter: TimefilterContract, + private getSavedVisualizationsLoader: () => SavedVisualizations + ) { super({ savedObjectMetaData: { name: i18n.translate('visualizations.savedObjectName', { defaultMessage: 'Visualization' }), @@ -114,6 +118,7 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory< const indexPattern = await getIndexPattern(savedObject); const indexPatterns = indexPattern ? [indexPattern] : []; return new VisualizeEmbeddable( + this.timefilter, { savedVisualization: savedObject, indexPatterns, diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts index a948757d7bd83..9fb87cadb2983 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts @@ -56,6 +56,7 @@ const createInstance = async () => { const plugin = new VisualizationsPlugin({} as PluginInitializerContext); const setup = plugin.setup(coreMock.createSetup(), { + data: dataPluginMock.createSetupContract(), expressions: expressionsPluginMock.createSetupContract(), embeddable: embeddablePluginMock.createStartContract(), usageCollection: usageCollectionPluginMock.createSetupContract(), diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts index 36c04923e3fd0..20bed59faad88 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts @@ -36,7 +36,10 @@ import { ExpressionsSetup } from '../../../../../../plugins/expressions/public'; import { IEmbeddableSetup } from '../../../../../../plugins/embeddable/public'; import { visualization as visualizationFunction } from './expressions/visualization_function'; import { visualization as visualizationRenderer } from './expressions/visualization_renderer'; -import { DataPublicPluginStart } from '../../../../../../plugins/data/public'; +import { + DataPublicPluginSetup, + DataPublicPluginStart, +} from '../../../../../../plugins/data/public'; import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/public'; import { createSavedVisLoader, @@ -65,6 +68,7 @@ export interface VisualizationsSetupDeps { expressions: ExpressionsSetup; embeddable: IEmbeddableSetup; usageCollection: UsageCollectionSetup; + data: DataPublicPluginSetup; } export interface VisualizationsStartDeps { @@ -95,7 +99,7 @@ export class VisualizationsPlugin public setup( core: CoreSetup, - { expressions, embeddable, usageCollection }: VisualizationsSetupDeps + { expressions, embeddable, usageCollection, data }: VisualizationsSetupDeps ): VisualizationsSetup { setUISettings(core.uiSettings); setUsageCollector(usageCollection); @@ -103,7 +107,10 @@ export class VisualizationsPlugin expressions.registerFunction(visualizationFunction); expressions.registerRenderer(visualizationRenderer); - const embeddableFactory = new VisualizeEmbeddableFactory(this.getSavedVisualizationsLoader); + const embeddableFactory = new VisualizeEmbeddableFactory( + data.query.timefilter.timefilter, + this.getSavedVisualizationsLoader + ); embeddable.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory); return { diff --git a/src/plugins/data/public/query/timefilter/timefilter_service.mock.ts b/src/plugins/data/public/query/timefilter/timefilter_service.mock.ts index 2923cee60f898..80c13464ad98a 100644 --- a/src/plugins/data/public/query/timefilter/timefilter_service.mock.ts +++ b/src/plugins/data/public/query/timefilter/timefilter_service.mock.ts @@ -18,6 +18,7 @@ */ import { TimefilterService, TimeHistoryContract, TimefilterContract } from '.'; +import { Observable } from 'rxjs'; export type TimefilterServiceClientContract = PublicMethodsOf<TimefilterService>; @@ -28,7 +29,7 @@ const createSetupContractMock = () => { getEnabledUpdated$: jest.fn(), getTimeUpdate$: jest.fn(), getRefreshIntervalUpdate$: jest.fn(), - getAutoRefreshFetch$: jest.fn(), + getAutoRefreshFetch$: jest.fn(() => new Observable<unknown>()), getFetch$: jest.fn(), getTime: jest.fn(), setTime: jest.fn(), diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx index a07bd475cdfcb..55363ebe4d8f3 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx @@ -4,10 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Subject } from 'rxjs'; import { Embeddable } from './embeddable'; import { ReactExpressionRendererProps } from 'src/plugins/expressions/public'; -import { Query, TimeRange, Filter } from 'src/plugins/data/public'; +import { Query, TimeRange, Filter, TimefilterContract } from 'src/plugins/data/public'; import { Document } from '../../persistence'; +import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; jest.mock('../../../../../../../src/plugins/inspector/public/', () => ({ isAvailable: false, @@ -44,6 +46,7 @@ describe('embeddable', () => { it('should render expression with expression renderer', () => { const embeddable = new Embeddable( + dataPluginMock.createSetupContract().query.timefilter.timefilter, expressionRenderer, { editUrl: '', @@ -64,6 +67,7 @@ describe('embeddable', () => { const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: false } }]; const embeddable = new Embeddable( + dataPluginMock.createSetupContract().query.timefilter.timefilter, expressionRenderer, { editUrl: '', @@ -89,6 +93,7 @@ describe('embeddable', () => { const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: false } }]; const embeddable = new Embeddable( + dataPluginMock.createSetupContract().query.timefilter.timefilter, expressionRenderer, { editUrl: '', @@ -112,6 +117,7 @@ describe('embeddable', () => { const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: true } }]; const embeddable = new Embeddable( + dataPluginMock.createSetupContract().query.timefilter.timefilter, expressionRenderer, { editUrl: '', @@ -130,4 +136,31 @@ describe('embeddable', () => { expect(expressionRenderer).toHaveBeenCalledTimes(1); }); + + it('should re-render on auto refresh fetch observable', () => { + const timeRange: TimeRange = { from: 'now-15d', to: 'now' }; + const query: Query = { language: 'kquery', query: '' }; + const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: true } }]; + + const autoRefreshFetchSubject = new Subject(); + const timefilter = ({ + getAutoRefreshFetch$: () => autoRefreshFetchSubject.asObservable(), + } as unknown) as TimefilterContract; + + const embeddable = new Embeddable( + timefilter, + expressionRenderer, + { + editUrl: '', + editable: true, + savedVis, + }, + { id: '123', timeRange, query, filters } + ); + embeddable.render(mountpoint); + + autoRefreshFetchSubject.next(); + + expect(expressionRenderer).toHaveBeenCalledTimes(2); + }); }); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index a3a55f26ff7c2..252ba5c9bc0bc 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -7,7 +7,13 @@ import _ from 'lodash'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { Query, TimeRange, Filter, IIndexPattern } from 'src/plugins/data/public'; +import { + Query, + TimeRange, + Filter, + IIndexPattern, + TimefilterContract, +} from 'src/plugins/data/public'; import { Subscription } from 'rxjs'; import { ReactExpressionRendererType } from '../../../../../../../src/plugins/expressions/public'; import { @@ -43,6 +49,7 @@ export class Embeddable extends AbstractEmbeddable<LensEmbeddableInput, LensEmbe private savedVis: Document; private domNode: HTMLElement | Element | undefined; private subscription: Subscription; + private autoRefreshFetchSubscription: Subscription; private currentContext: { timeRange?: TimeRange; @@ -52,6 +59,7 @@ export class Embeddable extends AbstractEmbeddable<LensEmbeddableInput, LensEmbe } = {}; constructor( + timefilter: TimefilterContract, expressionRenderer: ReactExpressionRendererType, { savedVis, editUrl, editable, indexPatterns }: LensEmbeddableConfiguration, initialInput: LensEmbeddableInput, @@ -76,6 +84,10 @@ export class Embeddable extends AbstractEmbeddable<LensEmbeddableInput, LensEmbe this.savedVis = savedVis; this.subscription = this.getInput$().subscribe(input => this.onContainerStateChanged(input)); this.onContainerStateChanged(initialInput); + + this.autoRefreshFetchSubscription = timefilter + .getAutoRefreshFetch$() + .subscribe(this.reload.bind(this)); } onContainerStateChanged(containerState: LensEmbeddableInput) { @@ -125,6 +137,7 @@ export class Embeddable extends AbstractEmbeddable<LensEmbeddableInput, LensEmbe if (this.subscription) { this.subscription.unsubscribe(); } + this.autoRefreshFetchSubscription.unsubscribe(); } reload() { diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts index e8bb8914fa292..d30ad62b385c2 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts @@ -11,7 +11,11 @@ import { SavedObjectsClientContract, } from 'kibana/public'; import { i18n } from '@kbn/i18n'; -import { IndexPatternsContract, IndexPattern } from '../../../../../../../src/plugins/data/public'; +import { + IndexPatternsContract, + IndexPattern, + TimefilterContract, +} from '../../../../../../../src/plugins/data/public'; import { ReactExpressionRendererType } from '../../../../../../../src/plugins/expressions/public'; import { EmbeddableFactory as AbstractEmbeddableFactory, @@ -27,6 +31,7 @@ export class EmbeddableFactory extends AbstractEmbeddableFactory { type = DOC_TYPE; constructor( + private timefilter: TimefilterContract, private coreHttp: HttpSetup, private capabilities: RecursiveReadonly<Capabilities>, private savedObjectsClient: SavedObjectsClientContract, @@ -85,6 +90,7 @@ export class EmbeddableFactory extends AbstractEmbeddableFactory { ); return new Embeddable( + this.timefilter, this.expressionRenderer, { savedVis, diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/mocks.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/mocks.tsx index cd121a1f96a2b..e606c69c8c386 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/mocks.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/mocks.tsx @@ -14,6 +14,7 @@ import { embeddablePluginMock } from '../../../../../../src/plugins/embeddable/p import { expressionsPluginMock } from '../../../../../../src/plugins/expressions/public/mocks'; import { DatasourcePublicAPI, FramePublicAPI, Datasource, Visualization } from '../types'; import { EditorFrameSetupPlugins, EditorFrameStartPlugins } from './service'; +import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks'; export function createMockVisualization(): jest.Mocked<Visualization> { return { @@ -103,7 +104,7 @@ export function createExpressionRendererMock(): jest.Mock< export function createMockSetupDependencies() { return ({ - data: {}, + data: dataPluginMock.createSetupContract(), embeddable: embeddablePluginMock.createSetupContract(), expressions: expressionsPluginMock.createSetupContract(), } as unknown) as MockedSetupDependencies; @@ -111,11 +112,7 @@ export function createMockSetupDependencies() { export function createMockStartDependencies() { return ({ - data: { - indexPatterns: { - indexPatterns: {}, - }, - }, + data: dataPluginMock.createSetupContract(), embeddable: embeddablePluginMock.createStartContract(), expressions: expressionsPluginMock.createStartContract(), } as unknown) as MockedStartDependencies; diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/service.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/service.tsx index 9a3d724705a1a..7a0bb3a2cc50f 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/service.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/service.tsx @@ -79,6 +79,7 @@ export class EditorFrameService { plugins.embeddable.registerEmbeddableFactory( 'lens', new EmbeddableFactory( + plugins.data.query.timefilter.timefilter, core.http, core.application.capabilities, core.savedObjects.client,