Fix auto refresh in visualizations and lens (#57667) (#57800)

This commit is contained in:
Joe Reuter 2020-02-17 15:45:25 +01:00 committed by GitHub
parent ef7bf84503
commit e34ee17574
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 86 additions and 22 deletions

View file

@ -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();

View file

@ -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 = () => {

View file

@ -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,

View file

@ -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(),

View file

@ -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 {

View file

@ -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(),

View file

@ -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);
});
});

View file

@ -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() {

View file

@ -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,

View file

@ -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;

View file

@ -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,