Skip to content

Commit

Permalink
[State Management] - Remove GlobalState from dashboard (#55158) (#55979)
Browse files Browse the repository at this point in the history
Removes GlobalState from dashboard app
  • Loading branch information
Dosant authored Jan 27, 2020
1 parent 5238f64 commit 652b6ae
Show file tree
Hide file tree
Showing 20 changed files with 807 additions and 173 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,7 @@ import _ from 'lodash';
import { Subscription } from 'rxjs';
import { State } from 'ui/state_management/state';
import { FilterManager, esFilters } from '../../../../../../plugins/data/public';

import {
compareFilters,
COMPARE_ALL_OPTIONS,
// this whole file will soon be deprecated by new state management.
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../../plugins/data/public/query/filter_manager/lib/compare_filters';
import { compareFilters, COMPARE_ALL_OPTIONS } from '../../../../../../plugins/data/public';

type GetAppStateFunc = () => { filters?: esFilters.Filter[]; save?: () => void } | undefined | null;

Expand Down
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,32 +19,31 @@

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';
Expand All @@ -67,6 +66,8 @@ export interface RenderDeps {
embeddables: IEmbeddableStart;
localStorage: Storage;
share: SharePluginStart;
history: History;
kbnUrlStateStorage: IKbnUrlStateStorage;
}

let angularModuleInstance: IModule | null = null;
Expand All @@ -79,7 +80,9 @@ export const renderApp = (element: HTMLElement, appBasePath: string, deps: Rende
// custom routing stuff
initDashboardApp(angularModuleInstance, deps);
}

const $injector = mountDashboardApp(appBasePath, element);

return () => {
$injector.get('$rootScope').$destroy();
};
Expand Down Expand Up @@ -146,17 +149,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 { map } from 'rxjs/operators';
import { DashboardEmptyScreen, DashboardEmptyScreenProps } from './dashboard_empty_screen';

import {
Expand All @@ -32,16 +32,17 @@ import {
SavedObjectSaveOpts,
SaveResult,
showSaveModal,
State,
subscribeWithScope,
} from '../legacy_imports';
import { FilterStateManager } from '../../../../data/public';
import {
esFilters,
COMPARE_ALL_OPTIONS,
compareFilters,
IndexPattern,
IndexPatternsContract,
Query,
SavedQuery,
syncAppFilters,
syncQuery,
} from '../../../../../../plugins/data/public';

import {
Expand Down Expand Up @@ -82,7 +83,6 @@ export interface DashboardAppControllerDependencies extends RenderDeps {
$scope: DashboardAppScope;
$route: any;
$routeParams: any;
globalState: State;
indexPatterns: IndexPatternsContract;
dashboardConfig: any;
config: any;
Expand All @@ -99,7 +99,6 @@ export class DashboardAppController {
$scope,
$route,
$routeParams,
globalState,
dashboardConfig,
localStorage,
indexPatterns,
Expand All @@ -109,15 +108,21 @@ export class DashboardAppController {
embeddables,
share,
dashboardCapabilities,
npDataStart: {
query: {
filterManager,
timefilter: { timefilter },
},
},
npDataStart: { query: queryService },
core: { notifications, overlays, chrome, injectedMetadata, uiSettings, savedObjects, http },
history,
kbnUrlStateStorage,
}: DashboardAppControllerDependencies) {
const filterManager = queryService.filterManager;
const queryFilter = filterManager;
const timefilter = queryService.timefilter.timefilter;

// starts syncing `_g` portion of url with query services
// note: dashboard_state_manager.ts syncs `_a` portion of url
const {
stop: stopSyncingGlobalStateWithUrl,
hasInheritedQueryFromUrl: hasInheritedGlobalStateFromUrl,
} = syncQuery(queryService, kbnUrlStateStorage);

let lastReloadRequestTime = 0;

Expand All @@ -126,34 +131,23 @@ 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
);
const stopSyncingAppFilters = syncAppFilters(filterManager, {
set: filters => dashboardStateManager.setFilters(filters),
get: () => dashboardStateManager.appState.filters,
state$: dashboardStateManager.appState$.pipe(map(state => state.filters)),
});

// 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() && !hasInheritedGlobalStateFromUrl) {
dashboardStateManager.syncTimefilterWithDashboard(timefilter);
}
$scope.showSaveQuery = dashboardCapabilities.saveQuery as boolean;
Expand Down Expand Up @@ -316,8 +310,14 @@ export class DashboardAppController {
// This has to be first because handleDashboardContainerChanges causes
// appState.save which will cause refreshDashboardContainer to be called.

// Add filters modifies the object passed to it, hence the clone deep.
if (!_.isEqual(container.getInput().filters, queryFilter.getFilters())) {
if (
!compareFilters(
container.getInput().filters,
queryFilter.getFilters(),
COMPARE_ALL_OPTIONS
)
) {
// Add filters modifies the object passed to it, hence the clone deep.
queryFilter.addFilters(_.cloneDeep(container.getInput().filters));

dashboardStateManager.applyFilters($scope.model.query, container.getInput().filters);
Expand Down Expand Up @@ -411,19 +411,27 @@ export class DashboardAppController {

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

// Filters shouldn't be compared using regular isEqual
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
];
if (!_.isEqual(containerValue, appStateValue)) {
// cloneDeep hack is needed, as there are multiple place, where container's input mutated,
// but values from appStateValue are deeply frozen, as they can't be mutated directly
(differences as { [key: string]: unknown })[key] = _.cloneDeep(appStateValue);
(differences as { [key: string]: unknown })[key] = appStateValue;
}
});

return Object.values(differences).length === 0 ? undefined : differences;
// cloneDeep hack is needed, as there are multiple place, where container's input mutated,
// but values from appStateValue are deeply frozen, as they can't be mutated directly
return Object.values(differences).length === 0 ? undefined : _.cloneDeep(differences);
};

const refreshDashboardContainer = () => {
Expand Down Expand Up @@ -878,6 +886,8 @@ export class DashboardAppController {

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

Expand All @@ -891,9 +901,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 Down Expand Up @@ -52,9 +53,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 { Observable, 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 @@ -76,6 +75,10 @@ export class DashboardStateManager {
return this.stateContainer.get();
}

public get appState$(): Observable<DashboardAppState> {
return this.stateContainer.state$;
}

private readonly stateContainer: ReduxLikeStateContainer<
DashboardAppState,
DashboardAppStateTransitions
Expand All @@ -97,13 +100,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 +120,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 652b6ae

Please sign in to comment.