Remove chrome.navLinks.update (#99633)

This commit is contained in:
Josh Dover 2021-05-24 20:00:45 +02:00 committed by GitHub
parent 9793a8fefb
commit c9f7ab3f72
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 50 additions and 203 deletions

View file

@ -23,5 +23,4 @@ export interface ChromeNavLinks
| [getNavLinks$()](./kibana-plugin-core-public.chromenavlinks.getnavlinks_.md) | Get an observable for a sorted list of navlinks. |
| [has(id)](./kibana-plugin-core-public.chromenavlinks.has.md) | Check whether or not a navlink exists. |
| [showOnly(id)](./kibana-plugin-core-public.chromenavlinks.showonly.md) | Remove all navlinks except the one matching the given id. |
| [update(id, values)](./kibana-plugin-core-public.chromenavlinks.update.md) | Update the navlink for the given id with the updated attributes. Returns the updated navlink or <code>undefined</code> if it does not exist. |

View file

@ -1,30 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeNavLinks](./kibana-plugin-core-public.chromenavlinks.md) &gt; [update](./kibana-plugin-core-public.chromenavlinks.update.md)
## ChromeNavLinks.update() method
> Warning: This API is now obsolete.
>
> Uses the property when registering your application with [ApplicationSetup.register()](./kibana-plugin-core-public.applicationsetup.register.md) instead.
>
Update the navlink for the given id with the updated attributes. Returns the updated navlink or `undefined` if it does not exist.
<b>Signature:</b>
```typescript
update(id: string, values: ChromeNavLinkUpdateableFields): ChromeNavLink | undefined;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| id | <code>string</code> | |
| values | <code>ChromeNavLinkUpdateableFields</code> | |
<b>Returns:</b>
`ChromeNavLink | undefined`

View file

@ -1,12 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeNavLinkUpdateableFields](./kibana-plugin-core-public.chromenavlinkupdateablefields.md)
## ChromeNavLinkUpdateableFields type
<b>Signature:</b>
```typescript
export declare type ChromeNavLinkUpdateableFields = Partial<Pick<ChromeNavLink, 'disabled' | 'hidden' | 'url' | 'href'>>;
```

View file

@ -154,7 +154,6 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [ChromeBreadcrumb](./kibana-plugin-core-public.chromebreadcrumb.md) | |
| [ChromeHelpExtensionLinkBase](./kibana-plugin-core-public.chromehelpextensionlinkbase.md) | |
| [ChromeHelpExtensionMenuLink](./kibana-plugin-core-public.chromehelpextensionmenulink.md) | |
| [ChromeNavLinkUpdateableFields](./kibana-plugin-core-public.chromenavlinkupdateablefields.md) | |
| [FatalErrorsStart](./kibana-plugin-core-public.fatalerrorsstart.md) | FatalErrors stop the Kibana Public Core and displays a fatal error screen with details about the Kibana build and the error. |
| [HttpStart](./kibana-plugin-core-public.httpstart.md) | See [HttpSetup](./kibana-plugin-core-public.httpsetup.md) |
| [IToasts](./kibana-plugin-core-public.itoasts.md) | Methods for adding and removing global toast messages. See [ToastsApi](./kibana-plugin-core-public.toastsapi.md)<!-- -->. |

View file

@ -20,7 +20,6 @@ const createStartContractMock = () => {
get: jest.fn(),
getAll: jest.fn(),
showOnly: jest.fn(),
update: jest.fn(),
enableForcedAppSwitcherNavigation: jest.fn(),
getForceAppSwitcherNavigation$: jest.fn(),
},

View file

@ -16,7 +16,7 @@ export type {
ChromeHelpExtensionMenuGitHubLink,
} from './ui/header/header_help_menu';
export type { NavType } from './ui';
export type { ChromeNavLink, ChromeNavLinks, ChromeNavLinkUpdateableFields } from './nav_links';
export type { ChromeNavLink, ChromeNavLinks } from './nav_links';
export type {
ChromeRecentlyAccessed,
ChromeRecentlyAccessedHistoryItem,

View file

@ -7,5 +7,5 @@
*/
export { NavLinksService } from './nav_links_service';
export type { ChromeNavLink, ChromeNavLinkUpdateableFields } from './nav_link';
export type { ChromeNavLink } from './nav_link';
export type { ChromeNavLinks } from './nav_links_service';

View file

@ -6,7 +6,6 @@
* Side Public License, v 1.
*/
import { pick } from '@kbn/std';
import { AppCategory } from '../../';
/**
@ -81,11 +80,6 @@ export interface ChromeNavLink {
readonly hidden?: boolean;
}
/** @public */
export type ChromeNavLinkUpdateableFields = Partial<
Pick<ChromeNavLink, 'disabled' | 'hidden' | 'url' | 'href'>
>;
export class NavLinkWrapper {
public readonly id: string;
public readonly properties: Readonly<ChromeNavLink>;
@ -98,10 +92,4 @@ export class NavLinkWrapper {
this.id = properties.id;
this.properties = Object.freeze(properties);
}
public update(newProps: ChromeNavLinkUpdateableFields) {
// Enforce limited properties at runtime for JS code
newProps = pick(newProps, ['disabled', 'hidden', 'url', 'href']);
return new NavLinkWrapper({ ...this.properties, ...newProps });
}
}

View file

@ -73,13 +73,10 @@ describe('NavLinksService', () => {
const navLinkIds$ = start.getNavLinks$().pipe(map((links) => links.map((l) => l.id)));
const emittedLinks: string[][] = [];
navLinkIds$.subscribe((r) => emittedLinks.push(r));
start.update('app1', { href: '/foo' });
start.showOnly('app1');
service.stop();
expect(emittedLinks).toEqual([
['app2', 'app1'],
['app2', 'app1'],
]);
expect(emittedLinks).toEqual([['app2', 'app1'], ['app1']]);
});
it('completes when service is stopped', async () => {
@ -170,45 +167,6 @@ describe('NavLinksService', () => {
});
});
describe('#update()', () => {
it('updates the navlinks and returns the updated link', async () => {
expect(start.update('app2', { hidden: true })).toEqual(
expect.objectContaining({
hidden: true,
id: 'app2',
order: -10,
title: 'App 2',
euiIconType: 'canvasApp',
})
);
const hiddenLinkIds = await start
.getNavLinks$()
.pipe(
take(1),
map((links) => links.filter((l) => l.hidden).map((l) => l.id))
)
.toPromise();
expect(hiddenLinkIds).toEqual(['app2']);
});
it('returns undefined if link does not exist', () => {
expect(start.update('fake', { hidden: true })).toBeUndefined();
});
it('keeps the updated link when availableApps are re-emitted', async () => {
start.update('app2', { hidden: true });
mockAppService.applications$.next(mockAppService.applications$.value);
const hiddenLinkIds = await start
.getNavLinks$()
.pipe(
take(1),
map((links) => links.filter((l) => l.hidden).map((l) => l.id))
)
.toPromise();
expect(hiddenLinkIds).toEqual(['app2']);
});
});
describe('#enableForcedAppSwitcherNavigation()', () => {
it('flips #getForceAppSwitcherNavigation$()', async () => {
await expect(start.getForceAppSwitcherNavigation$().pipe(take(1)).toPromise()).resolves.toBe(

View file

@ -12,7 +12,7 @@ import { map, takeUntil } from 'rxjs/operators';
import { InternalApplicationStart } from '../../application';
import { HttpStart } from '../../http';
import { ChromeNavLink, ChromeNavLinkUpdateableFields, NavLinkWrapper } from './nav_link';
import { ChromeNavLink, NavLinkWrapper } from './nav_link';
import { toNavLink } from './to_nav_link';
interface StartDeps {
@ -58,18 +58,6 @@ export interface ChromeNavLinks {
*/
showOnly(id: string): void;
/**
* Update the navlink for the given id with the updated attributes.
* Returns the updated navlink or `undefined` if it does not exist.
*
* @deprecated Uses the {@link AppBase.updater$} property when registering
* your application with {@link ApplicationSetup.register} instead.
*
* @param id
* @param values
*/
update(id: string, values: ChromeNavLinkUpdateableFields): ChromeNavLink | undefined;
/**
* Enable forced navigation mode, which will trigger a page refresh
* when a nav link is clicked and only the hash is updated.
@ -109,6 +97,7 @@ export class NavLinksService {
// now that availableApps$ is an observable, we need to keep record of all
// manual link modifications to be able to re-apply then after every
// availableApps$ changes.
// Only in use by `showOnly` API, can be removed once dashboard_mode is removed in 8.0
const linkUpdaters$ = new BehaviorSubject<LinksUpdater[]>([]);
const navLinks$ = new BehaviorSubject<ReadonlyMap<string, NavLinkWrapper>>(new Map());
@ -153,25 +142,6 @@ export class NavLinksService {
linkUpdaters$.next([...linkUpdaters$.value, updater]);
},
update(id: string, values: ChromeNavLinkUpdateableFields) {
if (!this.has(id)) {
return;
}
const updater: LinksUpdater = (navLinks) =>
new Map(
[...navLinks.entries()].map(([linkId, link]) => {
return [linkId, link.id === id ? link.update(values) : link] as [
string,
NavLinkWrapper
];
})
);
linkUpdaters$.next([...linkUpdaters$.value, updater]);
return this.get(id);
},
enableForcedAppSwitcherNavigation() {
forceAppSwitcherNavigation$.next(true);
},

View file

@ -41,7 +41,6 @@ import {
ChromeNavControls,
ChromeNavLink,
ChromeNavLinks,
ChromeNavLinkUpdateableFields,
ChromeDocTitle,
ChromeStart,
ChromeRecentlyAccessed,
@ -298,7 +297,6 @@ export type {
ChromeNavControls,
ChromeNavLink,
ChromeNavLinks,
ChromeNavLinkUpdateableFields,
ChromeDocTitle,
ChromeRecentlyAccessed,
ChromeRecentlyAccessedHistoryItem,

View file

@ -332,15 +332,8 @@ export interface ChromeNavLinks {
getNavLinks$(): Observable<Array<Readonly<ChromeNavLink>>>;
has(id: string): boolean;
showOnly(id: string): void;
// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "AppBase"
//
// @deprecated
update(id: string, values: ChromeNavLinkUpdateableFields): ChromeNavLink | undefined;
}
// @public (undocumented)
export type ChromeNavLinkUpdateableFields = Partial<Pick<ChromeNavLink, 'disabled' | 'hidden' | 'url' | 'href'>>;
// @public
export interface ChromeRecentlyAccessed {
// Warning: (ae-unresolved-link) The @link reference could not be resolved: No member was found with name "basePath"

View file

@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n';
import type { ConfigSchema } from '.';
import {
AppMountParameters,
AppNavLinkStatus,
CoreSetup,
CoreStart,
DEFAULT_APP_CATEGORIES,
@ -40,7 +41,6 @@ import type {
} from '../../triggers_actions_ui/public';
import { registerApmAlerts } from './components/alerting/register_apm_alerts';
import { featureCatalogueEntry } from './featureCatalogueEntry';
import { toggleAppLinkInNav } from './toggleAppLinkInNav';
export type ApmPluginSetup = ReturnType<ApmPlugin['setup']>;
@ -193,6 +193,9 @@ export class ApmPlugin implements Plugin<ApmPluginSetup, ApmPluginStart> {
order: 8500,
euiIconType: 'logoObservability',
category: DEFAULT_APP_CATEGORIES.observability,
navLinkStatus: config.ui.enabled
? AppNavLinkStatus.default
: AppNavLinkStatus.hidden,
meta: {
keywords: [
'RUM',
@ -231,7 +234,5 @@ export class ApmPlugin implements Plugin<ApmPluginSetup, ApmPluginStart> {
return {};
}
public start(core: CoreStart, plugins: ApmPluginStartDeps) {
toggleAppLinkInNav(core, this.initializerContext.config.get());
}
public start(core: CoreStart, plugins: ApmPluginStartDeps) {}
}

View file

@ -1,15 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { CoreStart } from 'kibana/public';
import { ConfigSchema } from '.';
export function toggleAppLinkInNav(core: CoreStart, { ui }: ConfigSchema) {
if (ui.enabled === false) {
core.chrome.navLinks.update('apm', { hidden: true });
}
}

View file

@ -17,7 +17,7 @@ export function assertNever(x: never): never {
export interface GraphLicenseInformation {
showAppLink: boolean;
enableAppLink: boolean;
message: string;
message?: string;
}
export function checkLicense(license: ILicense | undefined): GraphLicenseInformation {
@ -53,20 +53,19 @@ export function checkLicense(license: ILicense | undefined): GraphLicenseInforma
return {
showAppLink: true,
enableAppLink: false,
message: check.message || '',
message: check.message,
};
case 'invalid':
case 'unavailable':
return {
showAppLink: false,
enableAppLink: false,
message: check.message || '',
message: check.message,
};
case 'valid':
return {
showAppLink: true,
enableAppLink: true,
message: '',
};
default:
return assertNever(check.state);

View file

@ -91,7 +91,10 @@ export const renderApp = ({ appBasePath, element, kibanaLegacy, ...deps }: Graph
const licenseAllowsToShowThisPage = info.showAppLink && info.enableAppLink;
if (!licenseAllowsToShowThisPage) {
deps.core.notifications.toasts.addDanger(info.message);
if (info.message) {
deps.core.notifications.toasts.addDanger(info.message);
}
// This has to happen in the next tick because otherwise the original navigation
// bringing us to the graph app in the first place
// never completes and the browser enters are redirect loop

View file

@ -6,9 +6,17 @@
*/
import { i18n } from '@kbn/i18n';
import { CoreSetup, CoreStart } from 'kibana/public';
import { AppMountParameters, Plugin } from 'src/core/public';
import { PluginInitializerContext } from 'kibana/public';
import { BehaviorSubject } from 'rxjs';
import {
AppNavLinkStatus,
AppUpdater,
CoreSetup,
CoreStart,
AppMountParameters,
Plugin,
PluginInitializerContext,
DEFAULT_APP_CATEGORIES,
} from '../../../../src/core/public';
import { Storage } from '../../../../src/plugins/kibana_utils/public';
import {
@ -18,7 +26,6 @@ import {
import { NavigationPublicPluginStart as NavigationStart } from '../../../../src/plugins/navigation/public';
import { DataPublicPluginStart } from '../../../../src/plugins/data/public';
import { toggleNavLink } from './services/toggle_nav_link';
import { LicensingPluginStart } from '../../licensing/public';
import { checkLicense } from '../common/check_license';
import {
@ -26,7 +33,6 @@ import {
HomePublicPluginSetup,
HomePublicPluginStart,
} from '../../../../src/plugins/home/public';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public';
import { ConfigSchema } from '../config';
import { SavedObjectsStart } from '../../../../src/plugins/saved_objects/public';
@ -45,6 +51,8 @@ export interface GraphPluginStartDependencies {
export class GraphPlugin
implements Plugin<void, void, GraphPluginSetupDependencies, GraphPluginStartDependencies> {
private readonly appUpdater$ = new BehaviorSubject<AppUpdater>(() => ({}));
constructor(private initializerContext: PluginInitializerContext<ConfigSchema>) {}
setup(core: CoreSetup<GraphPluginStartDependencies>, { home }: GraphPluginSetupDependencies) {
@ -77,6 +85,7 @@ export class GraphPlugin
appRoute: '/app/graph',
euiIconType: 'logoKibana',
category: DEFAULT_APP_CATEGORIES.kibana,
updater$: this.appUpdater$,
mount: async (params: AppMountParameters) => {
const [coreStart, pluginsStart] = await core.getStartServices();
coreStart.chrome.docTitle.change(
@ -112,7 +121,16 @@ export class GraphPlugin
start(core: CoreStart, { home, licensing }: GraphPluginStartDependencies) {
licensing.license$.subscribe((license) => {
const licenseInformation = checkLicense(license);
toggleNavLink(licenseInformation, core.chrome.navLinks);
this.appUpdater$.next(() => ({
navLinkStatus: licenseInformation.showAppLink
? licenseInformation.enableAppLink
? AppNavLinkStatus.visible
: AppNavLinkStatus.disabled
: AppNavLinkStatus.hidden,
tooltip: licenseInformation.showAppLink ? licenseInformation.message : undefined,
}));
if (home && !licenseInformation.enableAppLink) {
home.featureCatalogue.removeFeature('graph');
}

View file

@ -1,27 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ChromeNavLink, ChromeNavLinks } from 'kibana/public';
import { GraphLicenseInformation } from '../../common/check_license';
type Mutable<T> = { -readonly [P in keyof T]: T[P] };
type ChromeNavLinkUpdate = Mutable<Partial<ChromeNavLink>>;
export function toggleNavLink(
licenseInformation: GraphLicenseInformation,
navLinks: ChromeNavLinks
) {
const navLinkUpdates: ChromeNavLinkUpdate = {
hidden: !licenseInformation.showAppLink,
};
if (licenseInformation.showAppLink) {
navLinkUpdates.disabled = !licenseInformation.enableAppLink;
navLinkUpdates.tooltip = licenseInformation.message;
}
navLinks.update('graph', navLinkUpdates);
}

View file

@ -84,13 +84,19 @@ class MyPlugin {
import { LicensingPluginSetup } from '../licensing/public'
class MyPlugin {
setup(core: CoreSetup, deps: SetupDeps) {
const appUpdater$ = new BehaviorSubject<AppUpdater>(() => {});
core.application.register({
id: 'myApp',
updater$: appUpdater$,
});
deps.licensing.license$.subscribe(license => {
const { state, message } = license.check('myPlugin', 'gold')
const hasRequiredLicense = state === 'valid';
const showLinks = hasRequiredLicense && license.getFeature('name').isAvailable;
chrome.navLinks.update('myPlugin', {
hidden: !showLinks
appUpdater$.next(() => {
navLinkStatus: showLinks ? AppNavLinkStatus.visible : AppNavLinkStatus.hidden
});
})
}