Skip to content

Commit

Permalink
Disable url tracking for dashboard (elastic#55818)
Browse files Browse the repository at this point in the history
  • Loading branch information
flash1293 authored and Maja Grubic committed Feb 10, 2020
1 parent 2c9c67f commit ea9c84e
Show file tree
Hide file tree
Showing 31 changed files with 681 additions and 121 deletions.
7 changes: 1 addition & 6 deletions src/legacy/core_plugins/kibana/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,8 @@ export default function(kibana) {
}),
order: -1001,
url: `${kbnBaseUrl}#/dashboards`,
// The subUrlBase is the common substring of all urls for this app. If not given, it defaults to the url
// above. This app has to use a different subUrlBase, in addition to the url above, because "#/dashboard"
// routes to a page that creates a new dashboard. When we introduced a landing page, we needed to change
// the url above in order to preserve the original url for BWC. The subUrlBase helps the Chrome api nav
// to determine what url to use for the app link.
subUrlBase: `${kbnBaseUrl}#/dashboard`,
euiIconType: 'dashboardApp',
disableSubUrlTracking: true,
category: DEFAULT_APP_CATEGORIES.analyze,
},
{
Expand Down
70 changes: 41 additions & 29 deletions src/legacy/core_plugins/kibana/public/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,15 @@
* under the License.
*/

const topLevelConfig = require('../../../../../.eslintrc.js');
const path = require('path');

const topLevelRestricedZones = topLevelConfig.overrides.find(
override =>
override.files[0] === '**/*.{js,ts,tsx}' &&
Object.keys(override.rules)[0] === '@kbn/eslint/no-restricted-paths'
).rules['@kbn/eslint/no-restricted-paths'][1].zones;

/**
* Builds custom restricted paths configuration for the shimmed plugins within the kibana plugin.
* These custom rules extend the default checks in the top level `eslintrc.js` by also checking two other things:
Expand All @@ -28,34 +35,37 @@ const path = require('path');
* @returns zones configuration for the no-restricted-paths linter
*/
function buildRestrictedPaths(shimmedPlugins) {
return shimmedPlugins.map(shimmedPlugin => ([{
target: [
`src/legacy/core_plugins/kibana/public/${shimmedPlugin}/np_ready/**/*`,
],
from: [
'ui/**/*',
'src/legacy/ui/**/*',
'src/legacy/core_plugins/kibana/public/**/*',
'src/legacy/core_plugins/data/public/**/*',
'!src/legacy/core_plugins/data/public/index.ts',
`!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/**/*`,
],
allowSameFolder: false,
errorMessage: `${shimmedPlugin} is a shimmed plugin that is not allowed to import modules from the legacy platform. If you need legacy modules for the transition period, import them either in the legacy_imports, kibana_services or index module.`,
}, {
target: [
'src/**/*',
`!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/**/*`,
'x-pack/**/*',
],
from: [
`src/legacy/core_plugins/kibana/public/${shimmedPlugin}/**/*`,
`!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/index.ts`,
`!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/legacy.ts`,
],
allowSameFolder: false,
errorMessage: `kibana/public/${shimmedPlugin} is behaving like a NP plugin and does not allow deep imports. If you need something from within ${shimmedPlugin} in another plugin, consider re-exporting it from the top level index module`,
}])).reduce((acc, part) => [...acc, ...part], []);
return shimmedPlugins
.map(shimmedPlugin => [
{
target: [`src/legacy/core_plugins/kibana/public/${shimmedPlugin}/np_ready/**/*`],
from: [
'ui/**/*',
'src/legacy/ui/**/*',
'src/legacy/core_plugins/kibana/public/**/*',
'src/legacy/core_plugins/data/public/**/*',
'!src/legacy/core_plugins/data/public/index.ts',
`!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/**/*`,
],
allowSameFolder: false,
errorMessage: `${shimmedPlugin} is a shimmed plugin that is not allowed to import modules from the legacy platform. If you need legacy modules for the transition period, import them either in the legacy_imports, kibana_services or index module.`,
},
{
target: [
'src/**/*',
`!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/**/*`,
'x-pack/**/*',
],
from: [
`src/legacy/core_plugins/kibana/public/${shimmedPlugin}/**/*`,
`!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/index.ts`,
`!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/legacy.ts`,
],
allowSameFolder: false,
errorMessage: `kibana/public/${shimmedPlugin} is behaving like a NP plugin and does not allow deep imports. If you need something from within ${shimmedPlugin} in another plugin, consider re-exporting it from the top level index module`,
},
])
.reduce((acc, part) => [...acc, ...part], []);
}

module.exports = {
Expand All @@ -66,7 +76,9 @@ module.exports = {
'error',
{
basePath: path.resolve(__dirname, '../../../../../'),
zones: buildRestrictedPaths(['visualize', 'discover', 'dashboard', 'devTools', 'home']),
zones: topLevelRestricedZones.concat(
buildRestrictedPaths(['visualize', 'discover', 'dashboard', 'devTools', 'home'])
),
},
],
},
Expand Down
1 change: 1 addition & 0 deletions src/legacy/core_plugins/kibana/public/dashboard/legacy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ async function getAngularDependencies(): Promise<LegacyAngularInjectedDependenci
const instance = plugin({} as PluginInitializerContext);
instance.setup(npSetup.core, {
...npSetup.plugins,
npData: npSetup.plugins.data,
__LEGACY: {
getAngularDependencies,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ export const renderApp = (element: HTMLElement, appBasePath: string, deps: Rende
angularModuleInstance = createLocalAngularModule(deps.core, deps.navigation);
// global routing stuff
configureAppAngularModule(angularModuleInstance, deps.core as LegacyCoreStart, true);
// custom routing stuff
initDashboardApp(angularModuleInstance, deps);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import { searchSourceMock } from '../../../../../../../plugins/data/public/search/search_source/mocks';
import { searchSourceMock } from '../../../../../../../plugins/data/public/mocks';
import { SavedObjectDashboard } from '../../saved_dashboard/saved_dashboard';

export function getSavedDashboardMock(
Expand Down
65 changes: 60 additions & 5 deletions src/legacy/core_plugins/kibana/public/dashboard/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* under the License.
*/

import { BehaviorSubject } from 'rxjs';
import {
App,
CoreSetup,
Expand All @@ -28,7 +29,10 @@ import {
import { i18n } from '@kbn/i18n';
import { RenderDeps } from './np_ready/application';
import { DataStart } from '../../../data/public';
import { DataPublicPluginStart as NpDataStart } from '../../../../../plugins/data/public';
import {
DataPublicPluginStart as NpDataStart,
DataPublicPluginSetup as NpDataSetup,
} from '../../../../../plugins/data/public';
import { IEmbeddableStart } from '../../../../../plugins/embeddable/public';
import { Storage } from '../../../../../plugins/kibana_utils/public';
import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public';
Expand All @@ -38,8 +42,13 @@ import {
HomePublicPluginSetup,
} from '../../../../../plugins/home/public';
import { SharePluginStart } from '../../../../../plugins/share/public';
import { KibanaLegacySetup } from '../../../../../plugins/kibana_legacy/public';
import {
AngularRenderedAppUpdater,
KibanaLegacySetup,
} from '../../../../../plugins/kibana_legacy/public';
import { createSavedDashboardLoader } from './saved_dashboard/saved_dashboards';
import { createKbnUrlTracker } from '../../../../../plugins/kibana_utils/public';
import { getQueryStateContainer } from '../../../../../plugins/data/public';

export interface LegacyAngularInjectedDependencies {
dashboardConfig: any;
Expand All @@ -59,6 +68,7 @@ export interface DashboardPluginSetupDependencies {
};
home: HomePublicPluginSetup;
kibana_legacy: KibanaLegacySetup;
npData: NpDataSetup;
}

export class DashboardPlugin implements Plugin {
Expand All @@ -70,17 +80,46 @@ export class DashboardPlugin implements Plugin {
share: SharePluginStart;
} | null = null;

private appStateUpdater = new BehaviorSubject<AngularRenderedAppUpdater>(() => ({}));
private stopUrlTracking: (() => void) | undefined = undefined;

public setup(
core: CoreSetup,
{ __LEGACY: { getAngularDependencies }, home, kibana_legacy }: DashboardPluginSetupDependencies
{
__LEGACY: { getAngularDependencies },
home,
kibana_legacy,
npData,
}: DashboardPluginSetupDependencies
) {
const { querySyncStateContainer, stop: stopQuerySyncStateContainer } = getQueryStateContainer(
npData.query
);
const { appMounted, appUnMounted, stop: stopUrlTracker } = createKbnUrlTracker({
baseUrl: core.http.basePath.prepend('/app/kibana'),
defaultSubUrl: '#/dashboards',
storageKey: 'lastUrl:dashboard',
navLinkUpdater$: this.appStateUpdater,
toastNotifications: core.notifications.toasts,
stateParams: [
{
kbnUrlKey: '_g',
stateUpdate$: querySyncStateContainer.state$,
},
],
});
this.stopUrlTracking = () => {
stopQuerySyncStateContainer();
stopUrlTracker();
};
const app: App = {
id: '',
title: 'Dashboards',
mount: async ({ core: contextCore }, params) => {
if (this.startDependencies === null) {
throw new Error('not started yet');
}
appMounted();
const {
savedObjectsClient,
embeddables,
Expand Down Expand Up @@ -114,10 +153,20 @@ export class DashboardPlugin implements Plugin {
localStorage: new Storage(localStorage),
};
const { renderApp } = await import('./np_ready/application');
return renderApp(params.element, params.appBasePath, deps);
const unmount = renderApp(params.element, params.appBasePath, deps);
return () => {
unmount();
appUnMounted();
};
},
};
kibana_legacy.registerLegacyApp({ ...app, id: 'dashboard' });
kibana_legacy.registerLegacyApp({
...app,
id: 'dashboard',
// only register the updater in once app, otherwise all updates would happen twice
updater$: this.appStateUpdater.asObservable(),
navLinkId: 'kibana:dashboard',
});
kibana_legacy.registerLegacyApp({ ...app, id: 'dashboards' });

home.featureCatalogue.register({
Expand Down Expand Up @@ -147,4 +196,10 @@ export class DashboardPlugin implements Plugin {
share,
};
}

stop() {
if (this.stopUrlTracking) {
this.stopUrlTracking();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import PropTypes from 'prop-types';
import { Instruction } from './instruction';
import { ParameterForm } from './parameter_form';
import { Content } from './content';
import { getDisplayText } from '../../../../../../../../plugins/home/server/tutorials/instructions/instruction_variant';
import { getDisplayText } from '../../../../../../../../plugins/home/public';
import {
EuiTabs,
EuiTab,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@ export class LocalApplicationService {
})();
},
});

if (app.updater$) {
app.updater$.subscribe(updater => {
const updatedFields = updater(app);
if (updatedFields && updatedFields.activeUrl) {
npStart.core.chrome.navLinks.update(app.navLinkId || app.id, {
url: updatedFields.activeUrl,
});
}
});
}
});

npStart.plugins.kibana_legacy.getForwards().forEach(({ legacyAppId, newAppId, keepPrefix }) => {
Expand Down
2 changes: 1 addition & 1 deletion src/legacy/ui/public/chrome/api/nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export function initChromeNavApi(chrome: any, internals: NavInternals) {
// link.active and link.lastUrl properties
coreNavLinks
.getAll()
.filter(link => link.subUrlBase)
.filter(link => link.subUrlBase && !link.disableSubUrlTracking)
.forEach(link => {
coreNavLinks.update(link.id, {
subUrlBase: relativeToAbsolute(chrome.addBasePath(link.subUrlBase)),
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/data/public/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ const createStartContract = (): Start => {
return startContract;
};

export { searchSourceMock } from './search/mocks';

export const dataPluginMock = {
createSetupContract,
createStartContract,
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/public/query/state_sync/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@
* under the License.
*/

export { syncQuery } from './sync_query';
export { syncQuery, getQueryStateContainer } from './sync_query';
export { syncAppFilters } from './sync_app_filters';
67 changes: 66 additions & 1 deletion src/plugins/data/public/query/state_sync/sync_query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
import { QueryService, QueryStart } from '../query_service';
import { StubBrowserStorage } from 'test_utils/stub_browser_storage';
import { TimefilterContract } from '../timefilter';
import { QuerySyncState, syncQuery } from './sync_query';
import { getQueryStateContainer, QuerySyncState, syncQuery } from './sync_query';

const setupMock = coreMock.createSetup();
const startMock = coreMock.createStart();
Expand Down Expand Up @@ -163,4 +163,69 @@ describe('sync_query', () => {
expect(spy).not.toBeCalled();
stop();
});

describe('getQueryStateContainer', () => {
test('state is initialized with state from query service', () => {
const { stop, querySyncStateContainer, initialState } = getQueryStateContainer(
queryServiceStart
);
expect(querySyncStateContainer.getState()).toMatchInlineSnapshot(`
Object {
"filters": Array [],
"refreshInterval": Object {
"pause": true,
"value": 0,
},
"time": Object {
"from": "now-15m",
"to": "now",
},
}
`);
expect(initialState).toEqual(querySyncStateContainer.getState());
stop();
});

test('state takes initial overrides into account', () => {
const { stop, querySyncStateContainer, initialState } = getQueryStateContainer(
queryServiceStart,
{
time: { from: 'now-99d', to: 'now' },
}
);
expect(querySyncStateContainer.getState().time).toEqual({
from: 'now-99d',
to: 'now',
});
expect(initialState).toEqual(querySyncStateContainer.getState());
stop();
});

test('when filters change, state container contains updated global filters', () => {
const { stop, querySyncStateContainer } = getQueryStateContainer(queryServiceStart);
filterManager.setFilters([gF, aF]);
expect(querySyncStateContainer.getState().filters).toHaveLength(1);
stop();
});

test('when time range changes, state container contains updated time range', () => {
const { stop, querySyncStateContainer } = getQueryStateContainer(queryServiceStart);
timefilter.setTime({ from: 'now-30m', to: 'now' });
expect(querySyncStateContainer.getState().time).toEqual({
from: 'now-30m',
to: 'now',
});
stop();
});

test('when refresh interval changes, state container contains updated refresh interval', () => {
const { stop, querySyncStateContainer } = getQueryStateContainer(queryServiceStart);
timefilter.setRefreshInterval({ pause: true, value: 100 });
expect(querySyncStateContainer.getState().refreshInterval).toEqual({
pause: true,
value: 100,
});
stop();
});
});
});
Loading

0 comments on commit ea9c84e

Please sign in to comment.