Skip to content

Commit

Permalink
Remove GlobalState from Dashboard App
Browse files Browse the repository at this point in the history
  • Loading branch information
Dosant committed Jan 21, 2020
1 parent d3cef47 commit c567e32
Show file tree
Hide file tree
Showing 10 changed files with 301 additions and 148 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import chrome from 'ui/chrome';

export const legacyChrome = chrome;
export { State } from 'ui/state_management/state';
export { SavedObjectSaveOpts } from 'ui/saved_objects/types';
export { npSetup, npStart } from 'ui/new_platform';
export { IPrivate } from 'ui/private';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,38 +19,38 @@

import { EuiConfirmModal, EuiIcon } from '@elastic/eui';
import angular, { IModule } from 'angular';
import { History } from 'history';
import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular';
import {
AppMountContext,
ChromeStart,
IUiSettingsClient,
LegacyCoreStart,
SavedObjectsClientContract,
IUiSettingsClient,
} from 'kibana/public';
import { Storage } from '../../../../../../plugins/kibana_utils/public';
import { IKbnUrlStateStorage, Storage } from '../../../../../../plugins/kibana_utils/public';
import {
GlobalStateProvider,
StateManagementConfigProvider,
PrivateProvider,
EventsProvider,
PersistedState,
configureAppAngularModule,
confirmModalFactory,
createTopNavDirective,
createTopNavHelper,
PromiseServiceCreator,
EventsProvider,
IPrivate,
KbnUrlProvider,
PersistedState,
PrivateProvider,
PromiseServiceCreator,
RedirectWhenMissingProvider,
confirmModalFactory,
configureAppAngularModule,
SavedObjectLoader,
IPrivate,
StateManagementConfigProvider,
} from '../legacy_imports';

// @ts-ignore
import { initDashboardApp } from './legacy_app';
import { IEmbeddableStart } from '../../../../../../plugins/embeddable/public';
import { NavigationPublicPluginStart as NavigationStart } from '../../../../../../plugins/navigation/public';
import { DataPublicPluginStart as NpDataStart } from '../../../../../../plugins/data/public';
import { SharePluginStart } from '../../../../../../plugins/share/public';
import { initGlobalState } from './kbn_global_state';

export interface RenderDeps {
core: LegacyCoreStart;
Expand All @@ -67,21 +67,46 @@ export interface RenderDeps {
embeddables: IEmbeddableStart;
localStorage: Storage;
share: SharePluginStart;
kbnUrlStateStorage: IKbnUrlStateStorage;
history: History;

// hack: keeping the value nested
// as configureAppAngularModule() is called once and initialised with reference to 'hasInheritedGlobalState' ,
// so to be able to update underlying value, so angular controller could pick it up
// this param is used to determine if time filter saved with dashboard should be applied or not
hasInheritedGlobalState?: {
value: boolean;
};
}

let angularModuleInstance: IModule | null = null;
const hasInheritedGlobalStateRef: { value: boolean } = { value: false };

export const renderApp = (element: HTMLElement, appBasePath: string, deps: RenderDeps) => {
const { destroy: destroyGlobalState, hasInheritedGlobalState } = initGlobalState(
deps.kbnUrlStateStorage,
deps.npDataStart.query.filterManager,
deps.npDataStart.query.timefilter.timefilter
);

// hack: always keeping the latest 'hasInheritedGlobalState' value in the same object - hasInheritedGlobalStateRef
// this is needed so angular Controller picks up the latest value, as it has reference to the hasInheritedGlobalStateRef
hasInheritedGlobalStateRef.value = hasInheritedGlobalState;
deps.hasInheritedGlobalState = hasInheritedGlobalStateRef;

if (!angularModuleInstance) {
angularModuleInstance = createLocalAngularModule(deps.core, deps.navigation);
// global routing stuff
configureAppAngularModule(angularModuleInstance, deps.core as LegacyCoreStart, true);
// custom routing stuff
initDashboardApp(angularModuleInstance, deps);
}

const $injector = mountDashboardApp(appBasePath, element);

return () => {
$injector.get('$rootScope').$destroy();
destroyGlobalState();
};
};

Expand Down Expand Up @@ -146,17 +171,13 @@ function createLocalConfirmModalModule() {
}

function createLocalStateModule() {
angular
.module('app/dashboard/State', [
'app/dashboard/Private',
'app/dashboard/Config',
'app/dashboard/KbnUrl',
'app/dashboard/Promise',
'app/dashboard/PersistedState',
])
.service('globalState', function(Private: any) {
return Private(GlobalStateProvider);
});
angular.module('app/dashboard/State', [
'app/dashboard/Private',
'app/dashboard/Config',
'app/dashboard/KbnUrl',
'app/dashboard/Promise',
'app/dashboard/PersistedState',
]);
}

function createLocalPersistedStateModule() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,12 @@ export function initDashboardAppDirective(app: any, deps: RenderDeps) {
$route: any,
$routeParams: {
id?: string;
},
globalState: any
}
) =>
new DashboardAppController({
$route,
$scope,
$routeParams,
globalState,
config,
confirmModal,
indexPatterns: deps.npDataStart.indexPatterns,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import React from 'react';
import angular from 'angular';

import { Subscription } from 'rxjs';
import { createHashHistory } from 'history';
import { filter, map } from 'rxjs/operators';
import { DashboardEmptyScreen, DashboardEmptyScreenProps } from './dashboard_empty_screen';

import {
Expand All @@ -32,12 +32,11 @@ import {
SavedObjectSaveOpts,
SaveResult,
showSaveModal,
State,
subscribeWithScope,
} from '../legacy_imports';
import { FilterStateManager } from '../../../../data/public';
import {
esFilters,
FilterManager,
IndexPattern,
IndexPatternsContract,
Query,
Expand Down Expand Up @@ -77,12 +76,15 @@ import {
SavedObjectFinderUi,
} from '../../../../../../plugins/kibana_react/public';
import { removeQueryParam, unhashUrl } from '../../../../../../plugins/kibana_utils/public';
import {
COMPARE_ALL_OPTIONS,
compareFilters,
} from '../../../../../../plugins/data/public/query/filter_manager/lib/compare_filters';

export interface DashboardAppControllerDependencies extends RenderDeps {
$scope: DashboardAppScope;
$route: any;
$routeParams: any;
globalState: State;
indexPatterns: IndexPatternsContract;
dashboardConfig: any;
config: any;
Expand All @@ -99,7 +101,6 @@ export class DashboardAppController {
$scope,
$route,
$routeParams,
globalState,
dashboardConfig,
localStorage,
indexPatterns,
Expand All @@ -116,6 +117,9 @@ export class DashboardAppController {
},
},
core: { notifications, overlays, chrome, injectedMetadata, uiSettings, savedObjects, http },
history,
kbnUrlStateStorage,
hasInheritedGlobalState,
}: DashboardAppControllerDependencies) {
const queryFilter = filterManager;

Expand All @@ -126,34 +130,59 @@ export class DashboardAppController {
chrome.docTitle.change(dash.title);
}

const history = createHashHistory();
const dashboardStateManager = new DashboardStateManager({
savedDashboard: dash,
useHashedUrl: config.get('state:storeInSessionStorage'),
hideWriteControls: dashboardConfig.getHideWriteControls(),
kibanaVersion: injectedMetadata.getKibanaVersion(),
kbnUrlStateStorage,
history,
});

const filterStateManager = new FilterStateManager(
globalState,
() => {
// Temporary AppState replacement
return {
set filters(_filters: esFilters.Filter[]) {
dashboardStateManager.setFilters(_filters);
},
get filters() {
return dashboardStateManager.appState.filters;
},
};
},
filterManager
);
// make sure initial filters are picked up from url
filterManager.setFilters([
...filterManager.getGlobalFilters(),
..._.cloneDeep(dashboardStateManager.appState.filters),
]);

// this should go away after: https://github.com/elastic/kibana/issues/55339
// is resolved
// sync filterManager's appFilters with dashboardStateManager
// dashboardStateManager then sync it to url '_a'
const appFiltersSubscription = filterManager
.getUpdates$()
.pipe(
map(() => filterManager.getAppFilters()),
filter(
// continue only if app state filters updated
appFilters =>
!compareFilters(appFilters, dashboardStateManager.appState.filters, COMPARE_ALL_OPTIONS)
)
)
.subscribe(appFilters => {
dashboardStateManager.setFilters(appFilters);
});

// this should go away after: https://github.com/elastic/kibana/issues/55339
// is resolved
// if appFilters in dashboardStateManager changed (e.g browser history update),
// sync it to filterManager
dashboardStateManager.registerChangeListener(() => {
if (
!compareFilters(
dashboardStateManager.appState.filters,
filterManager.getAppFilters(),
COMPARE_ALL_OPTIONS
)
) {
const newAppFilters = _.cloneDeep(dashboardStateManager.appState.filters);
FilterManager.setFiltersStore(newAppFilters, esFilters.FilterStateStore.APP_STATE);
filterManager.setFilters([...filterManager.getGlobalFilters(), ...newAppFilters]);
}
});

// The hash check is so we only update the time filter on dashboard open, not during
// normal cross app navigation.
if (dashboardStateManager.getIsTimeSavedWithDashboard() && !globalState.$inheritedGlobalState) {
if (dashboardStateManager.getIsTimeSavedWithDashboard() && !hasInheritedGlobalState?.value) {
dashboardStateManager.syncTimefilterWithDashboard(timefilter);
}
$scope.showSaveQuery = dashboardCapabilities.saveQuery as boolean;
Expand Down Expand Up @@ -411,7 +440,17 @@ export class DashboardAppController {

const containerInput = dashboardContainer.getInput();
const differences: Partial<DashboardContainerInput> = {};
Object.keys(containerInput).forEach(key => {

// Filters can't be compared using regular isEqual
// this should go away after: https://github.com/elastic/kibana/issues/55339
// is resolved
if (
!compareFilters(containerInput.filters, appStateDashboardInput.filters, COMPARE_ALL_OPTIONS)
) {
differences.filters = appStateDashboardInput.filters;
}

Object.keys(_.omit(containerInput, 'filters')).forEach(key => {
const containerValue = (containerInput as { [key: string]: unknown })[key];
const appStateValue = ((appStateDashboardInput as unknown) as { [key: string]: unknown })[
key
Expand Down Expand Up @@ -875,6 +914,7 @@ export class DashboardAppController {

$scope.$on('$destroy', () => {
updateSubscription.unsubscribe();
appFiltersSubscription.unsubscribe();
visibleSubscription.unsubscribe();
$scope.timefilterSubscriptions$.unsubscribe();

Expand All @@ -888,9 +928,6 @@ export class DashboardAppController {
if (dashboardContainer) {
dashboardContainer.destroy();
}
if (filterStateManager) {
filterStateManager.destroy();
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { DashboardStateManager } from './dashboard_state_manager';
import { getSavedDashboardMock } from '../__tests__';
import { InputTimeRange, TimefilterContract, TimeRange } from 'src/plugins/data/public';
import { ViewMode } from 'src/plugins/embeddable/public';
import { createKbnUrlStateStorage } from 'src/plugins/kibana_utils/public';

jest.mock('ui/agg_types', () => ({
aggTypes: {
Expand All @@ -48,9 +49,9 @@ describe('DashboardState', function() {
function initDashboardState() {
dashboardState = new DashboardStateManager({
savedDashboard,
useHashedUrl: false,
hideWriteControls: false,
kibanaVersion: '7.0.0',
kbnUrlStateStorage: createKbnUrlStateStorage(),
history: createBrowserHistory(),
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@

import { i18n } from '@kbn/i18n';
import _ from 'lodash';
import { History } from 'history';
import { Subscription } from 'rxjs';
import { Moment } from 'moment';
import { History } from 'history';

import { DashboardContainer } from 'src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public';
import { ViewMode } from '../../../../../../plugins/embeddable/public';
Expand All @@ -44,7 +44,6 @@ import {
SavedDashboardPanel,
} from './types';
import {
createKbnUrlStateStorage,
createStateContainer,
IKbnUrlStateStorage,
ISyncStateRef,
Expand Down Expand Up @@ -97,13 +96,13 @@ export class DashboardStateManager {
savedDashboard,
hideWriteControls,
kibanaVersion,
useHashedUrl,
kbnUrlStateStorage,
history,
}: {
savedDashboard: SavedObjectDashboard;
hideWriteControls: boolean;
kibanaVersion: string;
useHashedUrl: boolean;
kbnUrlStateStorage: IKbnUrlStateStorage;
history: History;
}) {
this.history = history;
Expand All @@ -117,7 +116,7 @@ export class DashboardStateManager {
kibanaVersion
);

this.kbnUrlStateStorage = createKbnUrlStateStorage({ useHash: useHashedUrl, history });
this.kbnUrlStateStorage = kbnUrlStateStorage;

// setup initial state by merging defaults with state from url
// also run migration, as state in url could be of older version
Expand Down
Loading

0 comments on commit c567e32

Please sign in to comment.