Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[State Management] - Remove GlobalState from dashboard #55158

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 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 { 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