mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Dashboard] Remove Legacy Dashboard Only Mode (#108103)
Remove all dashboard only mode code and tests. Align dashboard permissions to use showWriteControls only
This commit is contained in:
parent
92ec225cfd
commit
95463f47f3
57 changed files with 132 additions and 1528 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -144,7 +144,6 @@
|
|||
/x-pack/plugins/dashboard_enhanced/ @elastic/kibana-presentation
|
||||
/x-pack/test/functional/apps/canvas/ @elastic/kibana-presentation
|
||||
#CC# /src/plugins/kibana_react/public/code_editor/ @elastic/kibana-presentation
|
||||
#CC# /x-pack/plugins/dashboard_mode @elastic/kibana-presentation
|
||||
|
||||
# Machine Learning
|
||||
/x-pack/plugins/ml/ @elastic/ml-ui
|
||||
|
|
|
@ -1,238 +0,0 @@
|
|||
{
|
||||
"id": "dashboardMode",
|
||||
"client": {
|
||||
"classes": [],
|
||||
"functions": [],
|
||||
"interfaces": [],
|
||||
"enums": [],
|
||||
"misc": [],
|
||||
"objects": []
|
||||
},
|
||||
"server": {
|
||||
"classes": [
|
||||
{
|
||||
"parentPluginId": "dashboardMode",
|
||||
"id": "def-server.DashboardModeServerPlugin",
|
||||
"type": "Class",
|
||||
"tags": [],
|
||||
"label": "DashboardModeServerPlugin",
|
||||
"description": [],
|
||||
"signature": [
|
||||
{
|
||||
"pluginId": "dashboardMode",
|
||||
"scope": "server",
|
||||
"docId": "kibDashboardModePluginApi",
|
||||
"section": "def-server.DashboardModeServerPlugin",
|
||||
"text": "DashboardModeServerPlugin"
|
||||
},
|
||||
" implements ",
|
||||
{
|
||||
"pluginId": "core",
|
||||
"scope": "server",
|
||||
"docId": "kibCorePluginApi",
|
||||
"section": "def-server.Plugin",
|
||||
"text": "Plugin"
|
||||
},
|
||||
"<void, void, object, object>"
|
||||
],
|
||||
"path": "x-pack/plugins/dashboard_mode/server/plugin.ts",
|
||||
"deprecated": false,
|
||||
"children": [
|
||||
{
|
||||
"parentPluginId": "dashboardMode",
|
||||
"id": "def-server.DashboardModeServerPlugin.Unnamed",
|
||||
"type": "Function",
|
||||
"tags": [],
|
||||
"label": "Constructor",
|
||||
"description": [],
|
||||
"signature": [
|
||||
"any"
|
||||
],
|
||||
"path": "x-pack/plugins/dashboard_mode/server/plugin.ts",
|
||||
"deprecated": false,
|
||||
"children": [
|
||||
{
|
||||
"parentPluginId": "dashboardMode",
|
||||
"id": "def-server.DashboardModeServerPlugin.Unnamed.$1",
|
||||
"type": "Object",
|
||||
"tags": [],
|
||||
"label": "initializerContext",
|
||||
"description": [],
|
||||
"signature": [
|
||||
{
|
||||
"pluginId": "core",
|
||||
"scope": "server",
|
||||
"docId": "kibCorePluginApi",
|
||||
"section": "def-server.PluginInitializerContext",
|
||||
"text": "PluginInitializerContext"
|
||||
},
|
||||
"<unknown>"
|
||||
],
|
||||
"path": "x-pack/plugins/dashboard_mode/server/plugin.ts",
|
||||
"deprecated": false,
|
||||
"isRequired": true
|
||||
}
|
||||
],
|
||||
"returnComment": []
|
||||
},
|
||||
{
|
||||
"parentPluginId": "dashboardMode",
|
||||
"id": "def-server.DashboardModeServerPlugin.setup",
|
||||
"type": "Function",
|
||||
"tags": [],
|
||||
"label": "setup",
|
||||
"description": [],
|
||||
"signature": [
|
||||
"(core: ",
|
||||
{
|
||||
"pluginId": "core",
|
||||
"scope": "server",
|
||||
"docId": "kibCorePluginApi",
|
||||
"section": "def-server.CoreSetup",
|
||||
"text": "CoreSetup"
|
||||
},
|
||||
"<object, unknown>, { security }: DashboardModeServerSetupDependencies) => void"
|
||||
],
|
||||
"path": "x-pack/plugins/dashboard_mode/server/plugin.ts",
|
||||
"deprecated": false,
|
||||
"children": [
|
||||
{
|
||||
"parentPluginId": "dashboardMode",
|
||||
"id": "def-server.DashboardModeServerPlugin.setup.$1",
|
||||
"type": "Object",
|
||||
"tags": [],
|
||||
"label": "core",
|
||||
"description": [],
|
||||
"signature": [
|
||||
{
|
||||
"pluginId": "core",
|
||||
"scope": "server",
|
||||
"docId": "kibCorePluginApi",
|
||||
"section": "def-server.CoreSetup",
|
||||
"text": "CoreSetup"
|
||||
},
|
||||
"<object, unknown>"
|
||||
],
|
||||
"path": "x-pack/plugins/dashboard_mode/server/plugin.ts",
|
||||
"deprecated": false,
|
||||
"isRequired": true
|
||||
},
|
||||
{
|
||||
"parentPluginId": "dashboardMode",
|
||||
"id": "def-server.DashboardModeServerPlugin.setup.$2",
|
||||
"type": "Object",
|
||||
"tags": [],
|
||||
"label": "{ security }",
|
||||
"description": [],
|
||||
"signature": [
|
||||
"DashboardModeServerSetupDependencies"
|
||||
],
|
||||
"path": "x-pack/plugins/dashboard_mode/server/plugin.ts",
|
||||
"deprecated": false,
|
||||
"isRequired": true
|
||||
}
|
||||
],
|
||||
"returnComment": []
|
||||
},
|
||||
{
|
||||
"parentPluginId": "dashboardMode",
|
||||
"id": "def-server.DashboardModeServerPlugin.start",
|
||||
"type": "Function",
|
||||
"tags": [],
|
||||
"label": "start",
|
||||
"description": [],
|
||||
"signature": [
|
||||
"(core: ",
|
||||
{
|
||||
"pluginId": "core",
|
||||
"scope": "server",
|
||||
"docId": "kibCorePluginApi",
|
||||
"section": "def-server.CoreStart",
|
||||
"text": "CoreStart"
|
||||
},
|
||||
") => void"
|
||||
],
|
||||
"path": "x-pack/plugins/dashboard_mode/server/plugin.ts",
|
||||
"deprecated": false,
|
||||
"children": [
|
||||
{
|
||||
"parentPluginId": "dashboardMode",
|
||||
"id": "def-server.DashboardModeServerPlugin.start.$1",
|
||||
"type": "Object",
|
||||
"tags": [],
|
||||
"label": "core",
|
||||
"description": [],
|
||||
"signature": [
|
||||
{
|
||||
"pluginId": "core",
|
||||
"scope": "server",
|
||||
"docId": "kibCorePluginApi",
|
||||
"section": "def-server.CoreStart",
|
||||
"text": "CoreStart"
|
||||
}
|
||||
],
|
||||
"path": "x-pack/plugins/dashboard_mode/server/plugin.ts",
|
||||
"deprecated": false,
|
||||
"isRequired": true
|
||||
}
|
||||
],
|
||||
"returnComment": []
|
||||
},
|
||||
{
|
||||
"parentPluginId": "dashboardMode",
|
||||
"id": "def-server.DashboardModeServerPlugin.stop",
|
||||
"type": "Function",
|
||||
"tags": [],
|
||||
"label": "stop",
|
||||
"description": [],
|
||||
"signature": [
|
||||
"() => void"
|
||||
],
|
||||
"path": "x-pack/plugins/dashboard_mode/server/plugin.ts",
|
||||
"deprecated": false,
|
||||
"children": [],
|
||||
"returnComment": []
|
||||
}
|
||||
],
|
||||
"initialIsOpen": false
|
||||
}
|
||||
],
|
||||
"functions": [],
|
||||
"interfaces": [],
|
||||
"enums": [],
|
||||
"misc": [],
|
||||
"objects": []
|
||||
},
|
||||
"common": {
|
||||
"classes": [],
|
||||
"functions": [],
|
||||
"interfaces": [],
|
||||
"enums": [],
|
||||
"misc": [],
|
||||
"objects": [
|
||||
{
|
||||
"parentPluginId": "dashboardMode",
|
||||
"id": "def-common.UI_SETTINGS",
|
||||
"type": "Object",
|
||||
"tags": [],
|
||||
"label": "UI_SETTINGS",
|
||||
"description": [],
|
||||
"path": "x-pack/plugins/dashboard_mode/common/constants.ts",
|
||||
"deprecated": false,
|
||||
"children": [
|
||||
{
|
||||
"parentPluginId": "dashboardMode",
|
||||
"id": "def-common.UI_SETTINGS.CONFIG_DASHBOARD_ONLY_MODE_ROLES",
|
||||
"type": "string",
|
||||
"tags": [],
|
||||
"label": "CONFIG_DASHBOARD_ONLY_MODE_ROLES",
|
||||
"description": [],
|
||||
"path": "x-pack/plugins/dashboard_mode/common/constants.ts",
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"initialIsOpen": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
---
|
||||
id: kibDashboardModePluginApi
|
||||
slug: /kibana-dev-docs/dashboardModePluginApi
|
||||
title: dashboardMode
|
||||
image: https://source.unsplash.com/400x175/?github
|
||||
summary: API docs for the dashboardMode plugin
|
||||
date: 2020-11-16
|
||||
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardMode']
|
||||
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
|
||||
---
|
||||
import dashboardModeObj from './dashboard_mode.json';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**Code health stats**
|
||||
|
||||
| Public API count | Any count | Items lacking comments | Missing exports |
|
||||
|-------------------|-----------|------------------------|-----------------|
|
||||
| 11 | 0 | 11 | 0 |
|
||||
|
||||
## Server
|
||||
|
||||
### Classes
|
||||
<DocDefinitionList data={dashboardModeObj.server.classes}/>
|
||||
|
||||
## Common
|
||||
|
||||
### Objects
|
||||
<DocDefinitionList data={dashboardModeObj.common.objects}/>
|
||||
|
|
@ -378,10 +378,6 @@ The client-side plugin configures following values:
|
|||
|Adds drilldown capabilities to dashboard. Owned by the Kibana App team.
|
||||
|
||||
|
||||
|{kib-repo}blob/{branch}/x-pack/plugins/dashboard_mode/README.md[dashboardMode]
|
||||
|The deprecated dashboard only mode.
|
||||
|
||||
|
||||
|{kib-repo}blob/{branch}/x-pack/plugins/data_enhanced/README.md[dataEnhanced]
|
||||
|The data_enhanced plugin is the x-pack counterpart to the src/plguins/data plugin.
|
||||
|
||||
|
|
|
@ -22,5 +22,4 @@ export interface ChromeNavLinks
|
|||
| [getForceAppSwitcherNavigation$()](./kibana-plugin-core-public.chromenavlinks.getforceappswitchernavigation_.md) | An observable of the forced app switcher state. |
|
||||
| [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. |
|
||||
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ChromeNavLinks](./kibana-plugin-core-public.chromenavlinks.md) > [showOnly](./kibana-plugin-core-public.chromenavlinks.showonly.md)
|
||||
|
||||
## ChromeNavLinks.showOnly() method
|
||||
|
||||
Remove all navlinks except the one matching the given id.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
showOnly(id: string): void;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| id | <code>string</code> | |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
`void`
|
||||
|
||||
## Remarks
|
||||
|
||||
NOTE: this is not reversible.
|
||||
|
|
@ -12,7 +12,6 @@ pageLoadAssetSize:
|
|||
crossClusterReplication: 65408
|
||||
dashboard: 374194
|
||||
dashboardEnhanced: 65646
|
||||
dashboardMode: 22716
|
||||
data: 824229
|
||||
dataEnhanced: 50420
|
||||
devTools: 38637
|
||||
|
|
|
@ -19,7 +19,6 @@ const createStartContractMock = () => {
|
|||
has: jest.fn(),
|
||||
get: jest.fn(),
|
||||
getAll: jest.fn(),
|
||||
showOnly: jest.fn(),
|
||||
enableForcedAppSwitcherNavigation: jest.fn(),
|
||||
getForceAppSwitcherNavigation$: jest.fn(),
|
||||
},
|
||||
|
|
|
@ -89,10 +89,8 @@ describe('NavLinksService', () => {
|
|||
const navLinkIds$ = start.getNavLinks$().pipe(map((links) => links.map((l) => l.id)));
|
||||
const emittedLinks: string[][] = [];
|
||||
navLinkIds$.subscribe((r) => emittedLinks.push(r));
|
||||
start.showOnly('app1');
|
||||
|
||||
service.stop();
|
||||
expect(emittedLinks).toEqual([['app2', 'app1', 'app2:deepApp2', 'app2:deepApp1'], ['app1']]);
|
||||
expect(emittedLinks).toEqual([['app2', 'app1', 'app2:deepApp2', 'app2:deepApp1']]);
|
||||
});
|
||||
|
||||
it('completes when service is stopped', async () => {
|
||||
|
@ -133,74 +131,6 @@ describe('NavLinksService', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('#showOnly()', () => {
|
||||
it('does nothing if link does not exist', async () => {
|
||||
start.showOnly('fake');
|
||||
expect(
|
||||
await start
|
||||
.getNavLinks$()
|
||||
.pipe(
|
||||
take(1),
|
||||
map((links) => links.map((l) => l.id))
|
||||
)
|
||||
.toPromise()
|
||||
).toEqual(['app2', 'app1', 'app2:deepApp2', 'app2:deepApp1']);
|
||||
});
|
||||
|
||||
it('does nothing on chromeless applications', async () => {
|
||||
start.showOnly('chromelessApp');
|
||||
expect(
|
||||
await start
|
||||
.getNavLinks$()
|
||||
.pipe(
|
||||
take(1),
|
||||
map((links) => links.map((l) => l.id))
|
||||
)
|
||||
.toPromise()
|
||||
).toEqual(['app2', 'app1', 'app2:deepApp2', 'app2:deepApp1']);
|
||||
});
|
||||
|
||||
it('removes all other links', async () => {
|
||||
start.showOnly('app2');
|
||||
expect(
|
||||
await start
|
||||
.getNavLinks$()
|
||||
.pipe(
|
||||
take(1),
|
||||
map((links) => links.map((l) => l.id))
|
||||
)
|
||||
.toPromise()
|
||||
).toEqual(['app2']);
|
||||
});
|
||||
|
||||
it('show only deep link', async () => {
|
||||
start.showOnly('app2:deepApp1');
|
||||
expect(
|
||||
await start
|
||||
.getNavLinks$()
|
||||
.pipe(
|
||||
take(1),
|
||||
map((links) => links.map((l) => l.id))
|
||||
)
|
||||
.toPromise()
|
||||
).toEqual(['app2:deepApp1']);
|
||||
});
|
||||
|
||||
it('still removes all other links when availableApps are re-emitted', async () => {
|
||||
start.showOnly('app2');
|
||||
mockAppService.applications$.next(mockAppService.applications$.value);
|
||||
expect(
|
||||
await start
|
||||
.getNavLinks$()
|
||||
.pipe(
|
||||
take(1),
|
||||
map((links) => links.map((l) => l.id))
|
||||
)
|
||||
.toPromise()
|
||||
).toEqual(['app2']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#enableForcedAppSwitcherNavigation()', () => {
|
||||
it('flips #getForceAppSwitcherNavigation$()', async () => {
|
||||
await expect(start.getForceAppSwitcherNavigation$().pipe(take(1)).toPromise()).resolves.toBe(
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import { sortBy } from 'lodash';
|
||||
import { BehaviorSubject, combineLatest, Observable, ReplaySubject } from 'rxjs';
|
||||
import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';
|
||||
import { map, takeUntil } from 'rxjs/operators';
|
||||
|
||||
import { InternalApplicationStart, PublicAppDeepLinkInfo, PublicAppInfo } from '../../application';
|
||||
|
@ -48,16 +48,6 @@ export interface ChromeNavLinks {
|
|||
*/
|
||||
has(id: string): boolean;
|
||||
|
||||
/**
|
||||
* Remove all navlinks except the one matching the given id.
|
||||
*
|
||||
* @remarks
|
||||
* NOTE: this is not reversible.
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
showOnly(id: string): void;
|
||||
|
||||
/**
|
||||
* Enable forced navigation mode, which will trigger a page refresh
|
||||
* when a nav link is clicked and only the hash is updated.
|
||||
|
@ -78,39 +68,25 @@ export interface ChromeNavLinks {
|
|||
getForceAppSwitcherNavigation$(): Observable<boolean>;
|
||||
}
|
||||
|
||||
type LinksUpdater = (navLinks: Map<string, NavLinkWrapper>) => Map<string, NavLinkWrapper>;
|
||||
|
||||
export class NavLinksService {
|
||||
private readonly stop$ = new ReplaySubject(1);
|
||||
|
||||
public start({ application, http }: StartDeps): ChromeNavLinks {
|
||||
const appLinks$ = application.applications$.pipe(
|
||||
map((apps) => {
|
||||
return new Map(
|
||||
[...apps]
|
||||
.filter(([, app]) => !app.chromeless)
|
||||
.reduce((navLinks: Array<[string, NavLinkWrapper]>, [appId, app]) => {
|
||||
navLinks.push(
|
||||
[appId, toNavLink(app, http.basePath)],
|
||||
...toNavDeepLinks(app, app.deepLinks, http.basePath)
|
||||
);
|
||||
return navLinks;
|
||||
}, [])
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
// 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());
|
||||
|
||||
combineLatest([appLinks$, linkUpdaters$])
|
||||
application.applications$
|
||||
.pipe(
|
||||
map(([appLinks, linkUpdaters]) => {
|
||||
return linkUpdaters.reduce((links, updater) => updater(links), appLinks);
|
||||
map((apps) => {
|
||||
return new Map(
|
||||
[...apps]
|
||||
.filter(([, app]) => !app.chromeless)
|
||||
.reduce((navLinks: Array<[string, NavLinkWrapper]>, [appId, app]) => {
|
||||
navLinks.push(
|
||||
[appId, toNavLink(app, http.basePath)],
|
||||
...toNavDeepLinks(app, app.deepLinks, http.basePath)
|
||||
);
|
||||
return navLinks;
|
||||
}, [])
|
||||
);
|
||||
})
|
||||
)
|
||||
.subscribe((navlinks) => {
|
||||
|
@ -137,17 +113,6 @@ export class NavLinksService {
|
|||
return navLinks$.value.has(id);
|
||||
},
|
||||
|
||||
showOnly(id: string) {
|
||||
if (!this.has(id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updater: LinksUpdater = (navLinks) =>
|
||||
new Map([...navLinks.entries()].filter(([linkId]) => linkId === id));
|
||||
|
||||
linkUpdaters$.next([...linkUpdaters$.value, updater]);
|
||||
},
|
||||
|
||||
enableForcedAppSwitcherNavigation() {
|
||||
forceAppSwitcherNavigation$.next(true);
|
||||
},
|
||||
|
|
|
@ -324,7 +324,6 @@ export interface ChromeNavLinks {
|
|||
getForceAppSwitcherNavigation$(): Observable<boolean>;
|
||||
getNavLinks$(): Observable<Array<Readonly<ChromeNavLink>>>;
|
||||
has(id: string): boolean;
|
||||
showOnly(id: string): void;
|
||||
}
|
||||
|
||||
// @public
|
||||
|
|
|
@ -80,7 +80,6 @@ export async function mountApp({
|
|||
data: dataStart,
|
||||
share: shareStart,
|
||||
embeddable: embeddableStart,
|
||||
kibanaLegacy: { dashboardConfig },
|
||||
savedObjectsTaggingOss,
|
||||
visualizations,
|
||||
presentationUtil,
|
||||
|
@ -117,12 +116,12 @@ export async function mountApp({
|
|||
allowByValueEmbeddables: initializerContext.config.get<DashboardFeatureFlagConfig>()
|
||||
.allowByValueEmbeddables,
|
||||
dashboardCapabilities: {
|
||||
hideWriteControls: dashboardConfig.getHideWriteControls(),
|
||||
show: Boolean(coreStart.application.capabilities.dashboard.show),
|
||||
saveQuery: Boolean(coreStart.application.capabilities.dashboard.saveQuery),
|
||||
createNew: Boolean(coreStart.application.capabilities.dashboard.createNew),
|
||||
mapsCapabilities: { save: Boolean(coreStart.application.capabilities.maps?.save) },
|
||||
createShortUrl: Boolean(coreStart.application.capabilities.dashboard.createShortUrl),
|
||||
showWriteControls: Boolean(coreStart.application.capabilities.dashboard.showWriteControls),
|
||||
visualizeCapabilities: { save: Boolean(coreStart.application.capabilities.visualize?.save) },
|
||||
storeSearchSession: Boolean(coreStart.application.capabilities.dashboard.storeSearchSession),
|
||||
},
|
||||
|
@ -251,7 +250,7 @@ export async function mountApp({
|
|||
);
|
||||
|
||||
addHelpMenuToAppChrome(dashboardServices.chrome, coreStart.docLinks);
|
||||
if (dashboardServices.dashboardCapabilities.hideWriteControls) {
|
||||
if (!dashboardServices.dashboardCapabilities.showWriteControls) {
|
||||
coreStart.chrome.setBadge({
|
||||
text: dashboardReadonlyBadge.getText(),
|
||||
tooltip: dashboardReadonlyBadge.getTooltip(),
|
||||
|
|
|
@ -79,7 +79,7 @@ const defaultCapabilities: DashboardAppCapabilities = {
|
|||
createNew: false,
|
||||
saveQuery: false,
|
||||
createShortUrl: false,
|
||||
hideWriteControls: true,
|
||||
showWriteControls: false,
|
||||
mapsCapabilities: { save: false },
|
||||
visualizeCapabilities: { save: false },
|
||||
storeSearchSession: true,
|
||||
|
|
|
@ -117,7 +117,7 @@ export class DashboardViewport extends React.Component<DashboardViewportProps, S
|
|||
<div className="dshDashboardEmptyScreen">
|
||||
<DashboardEmptyScreen
|
||||
isReadonlyMode={
|
||||
this.props.container.getInput().dashboardCapabilities?.hideWriteControls
|
||||
!this.props.container.getInput().dashboardCapabilities?.showWriteControls
|
||||
}
|
||||
isEditMode={isEditMode}
|
||||
uiSettings={this.context.services.uiSettings}
|
||||
|
|
|
@ -259,7 +259,7 @@ export const useDashboardAppState = ({
|
|||
const updateLastSavedState = () => {
|
||||
setLastSavedState(
|
||||
savedObjectToDashboardState({
|
||||
hideWriteControls: dashboardBuildContext.dashboardCapabilities.hideWriteControls,
|
||||
showWriteControls: dashboardBuildContext.dashboardCapabilities.showWriteControls,
|
||||
version: dashboardBuildContext.kibanaVersion,
|
||||
savedObjectsTagging,
|
||||
usageCollection,
|
||||
|
@ -341,7 +341,12 @@ export const useDashboardAppState = ({
|
|||
if (from && to) timefilter.setTime({ from, to });
|
||||
if (refreshInterval) timefilter.setRefreshInterval(refreshInterval);
|
||||
}
|
||||
dispatchDashboardStateChange(setDashboardState(lastSavedState));
|
||||
dispatchDashboardStateChange(
|
||||
setDashboardState({
|
||||
...lastSavedState,
|
||||
viewMode: ViewMode.VIEW,
|
||||
})
|
||||
);
|
||||
}, [lastSavedState, dashboardAppState, data.query.timefilter, dispatchDashboardStateChange]);
|
||||
|
||||
/**
|
||||
|
|
|
@ -27,7 +27,7 @@ import {
|
|||
|
||||
interface SavedObjectToDashboardStateProps {
|
||||
version: string;
|
||||
hideWriteControls: boolean;
|
||||
showWriteControls: boolean;
|
||||
savedDashboard: DashboardSavedObject;
|
||||
usageCollection: DashboardAppServices['usageCollection'];
|
||||
savedObjectsTagging: DashboardAppServices['savedObjectsTagging'];
|
||||
|
@ -55,9 +55,9 @@ interface StateToRawDashboardStateProps {
|
|||
*/
|
||||
export const savedObjectToDashboardState = ({
|
||||
version,
|
||||
hideWriteControls,
|
||||
savedDashboard,
|
||||
usageCollection,
|
||||
showWriteControls,
|
||||
savedObjectsTagging,
|
||||
}: SavedObjectToDashboardStateProps): DashboardState => {
|
||||
const rawState = migrateAppState(
|
||||
|
@ -70,7 +70,7 @@ export const savedObjectToDashboardState = ({
|
|||
description: savedDashboard.description || '',
|
||||
tags: getTagsFromSavedDashboard(savedDashboard, savedObjectsTagging),
|
||||
panels: savedDashboard.panelsJSON ? JSON.parse(savedDashboard.panelsJSON) : [],
|
||||
viewMode: savedDashboard.id || hideWriteControls ? ViewMode.VIEW : ViewMode.EDIT,
|
||||
viewMode: savedDashboard.id || showWriteControls ? ViewMode.EDIT : ViewMode.VIEW,
|
||||
options: savedDashboard.optionsJSON ? JSON.parse(savedDashboard.optionsJSON) : {},
|
||||
},
|
||||
version,
|
||||
|
|
|
@ -38,7 +38,7 @@ export const loadSavedDashboardState = async ({
|
|||
}: DashboardBuildContext & { savedDashboardId?: string }): Promise<
|
||||
LoadSavedDashboardStateReturn | undefined
|
||||
> => {
|
||||
const { hideWriteControls } = dashboardCapabilities;
|
||||
const { showWriteControls } = dashboardCapabilities;
|
||||
const { queryString } = query;
|
||||
|
||||
// BWC - remove for 8.0
|
||||
|
@ -66,12 +66,12 @@ export const loadSavedDashboardState = async ({
|
|||
const savedDashboardState = savedObjectToDashboardState({
|
||||
savedDashboard,
|
||||
usageCollection,
|
||||
hideWriteControls,
|
||||
showWriteControls,
|
||||
savedObjectsTagging,
|
||||
version: initializerContext.env.packageInfo.version,
|
||||
});
|
||||
|
||||
const isViewMode = hideWriteControls || Boolean(savedDashboard.id);
|
||||
const isViewMode = !showWriteControls || Boolean(savedDashboard.id);
|
||||
savedDashboardState.viewMode = isViewMode ? ViewMode.VIEW : ViewMode.EDIT;
|
||||
savedDashboardState.filters = cleanFiltersForSerialize(savedDashboardState.filters);
|
||||
savedDashboardState.query = migrateLegacyQuery(
|
||||
|
|
|
@ -19,7 +19,7 @@ describe('createSessionRestorationDataProvider', () => {
|
|||
getAppState: () =>
|
||||
savedObjectToDashboardState({
|
||||
version,
|
||||
hideWriteControls: false,
|
||||
showWriteControls: true,
|
||||
usageCollection: undefined,
|
||||
savedObjectsTagging: undefined,
|
||||
savedDashboard: getSavedDashboardMock(),
|
||||
|
|
|
@ -112,87 +112,6 @@ exports[`after fetch When given a title that matches multiple dashboards, filter
|
|||
</DashboardListing>
|
||||
`;
|
||||
|
||||
exports[`after fetch hideWriteControls 1`] = `
|
||||
<DashboardListing
|
||||
kbnUrlStateStorage={
|
||||
Object {
|
||||
"cancel": [Function],
|
||||
"change$": [Function],
|
||||
"get": [Function],
|
||||
"kbnUrlControls": Object {
|
||||
"cancel": [Function],
|
||||
"flush": [Function],
|
||||
"getPendingUrl": [Function],
|
||||
"listen": [Function],
|
||||
"update": [Function],
|
||||
"updateAsync": [Function],
|
||||
},
|
||||
"set": [Function],
|
||||
}
|
||||
}
|
||||
redirectTo={[MockFunction]}
|
||||
>
|
||||
<TableListView
|
||||
emptyPrompt={
|
||||
<EuiEmptyPrompt
|
||||
body={
|
||||
<p>
|
||||
There are no available dashboards. To change your permissions to view the dashboards in this space, contact your administrator.
|
||||
</p>
|
||||
}
|
||||
iconType="glasses"
|
||||
title={
|
||||
<h1
|
||||
id="dashboardListingHeading"
|
||||
>
|
||||
No dashboards to view
|
||||
</h1>
|
||||
}
|
||||
/>
|
||||
}
|
||||
entityName="dashboard"
|
||||
entityNamePlural="dashboards"
|
||||
findItems={[Function]}
|
||||
headingId="dashboardListingHeading"
|
||||
initialFilter=""
|
||||
initialPageSize={20}
|
||||
listingLimit={100}
|
||||
rowHeader="title"
|
||||
searchFilters={Array []}
|
||||
tableCaption="Dashboards"
|
||||
tableColumns={
|
||||
Array [
|
||||
Object {
|
||||
"field": "title",
|
||||
"name": "Title",
|
||||
"render": [Function],
|
||||
"sortable": true,
|
||||
},
|
||||
Object {
|
||||
"field": "description",
|
||||
"name": "Description",
|
||||
"render": [Function],
|
||||
"sortable": true,
|
||||
},
|
||||
]
|
||||
}
|
||||
tableListTitle="Dashboards"
|
||||
toastNotifications={
|
||||
Object {
|
||||
"add": [MockFunction],
|
||||
"addDanger": [MockFunction],
|
||||
"addError": [MockFunction],
|
||||
"addInfo": [MockFunction],
|
||||
"addSuccess": [MockFunction],
|
||||
"addWarning": [MockFunction],
|
||||
"get$": [MockFunction],
|
||||
"remove": [MockFunction],
|
||||
}
|
||||
}
|
||||
/>
|
||||
</DashboardListing>
|
||||
`;
|
||||
|
||||
exports[`after fetch initialFilter 1`] = `
|
||||
<DashboardListing
|
||||
initialFilter="testFilter"
|
||||
|
@ -637,3 +556,84 @@ exports[`after fetch renders warning when listingLimit is exceeded 1`] = `
|
|||
/>
|
||||
</DashboardListing>
|
||||
`;
|
||||
|
||||
exports[`after fetch showWriteControls 1`] = `
|
||||
<DashboardListing
|
||||
kbnUrlStateStorage={
|
||||
Object {
|
||||
"cancel": [Function],
|
||||
"change$": [Function],
|
||||
"get": [Function],
|
||||
"kbnUrlControls": Object {
|
||||
"cancel": [Function],
|
||||
"flush": [Function],
|
||||
"getPendingUrl": [Function],
|
||||
"listen": [Function],
|
||||
"update": [Function],
|
||||
"updateAsync": [Function],
|
||||
},
|
||||
"set": [Function],
|
||||
}
|
||||
}
|
||||
redirectTo={[MockFunction]}
|
||||
>
|
||||
<TableListView
|
||||
emptyPrompt={
|
||||
<EuiEmptyPrompt
|
||||
body={
|
||||
<p>
|
||||
There are no available dashboards. To change your permissions to view the dashboards in this space, contact your administrator.
|
||||
</p>
|
||||
}
|
||||
iconType="glasses"
|
||||
title={
|
||||
<h1
|
||||
id="dashboardListingHeading"
|
||||
>
|
||||
No dashboards to view
|
||||
</h1>
|
||||
}
|
||||
/>
|
||||
}
|
||||
entityName="dashboard"
|
||||
entityNamePlural="dashboards"
|
||||
findItems={[Function]}
|
||||
headingId="dashboardListingHeading"
|
||||
initialFilter=""
|
||||
initialPageSize={20}
|
||||
listingLimit={100}
|
||||
rowHeader="title"
|
||||
searchFilters={Array []}
|
||||
tableCaption="Dashboards"
|
||||
tableColumns={
|
||||
Array [
|
||||
Object {
|
||||
"field": "title",
|
||||
"name": "Title",
|
||||
"render": [Function],
|
||||
"sortable": true,
|
||||
},
|
||||
Object {
|
||||
"field": "description",
|
||||
"name": "Description",
|
||||
"render": [Function],
|
||||
"sortable": true,
|
||||
},
|
||||
]
|
||||
}
|
||||
tableListTitle="Dashboards"
|
||||
toastNotifications={
|
||||
Object {
|
||||
"add": [MockFunction],
|
||||
"addDanger": [MockFunction],
|
||||
"addError": [MockFunction],
|
||||
"addInfo": [MockFunction],
|
||||
"addSuccess": [MockFunction],
|
||||
"addWarning": [MockFunction],
|
||||
"get$": [MockFunction],
|
||||
"remove": [MockFunction],
|
||||
}
|
||||
}
|
||||
/>
|
||||
</DashboardListing>
|
||||
`;
|
||||
|
|
|
@ -133,9 +133,9 @@ describe('after fetch', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('hideWriteControls', async () => {
|
||||
test('showWriteControls', async () => {
|
||||
const services = makeDefaultServices();
|
||||
services.dashboardCapabilities.hideWriteControls = true;
|
||||
services.dashboardCapabilities.showWriteControls = false;
|
||||
const { component } = mountWith({ services });
|
||||
// Ensure all promises resolve
|
||||
await new Promise((resolve) => process.nextTick(resolve));
|
||||
|
|
|
@ -87,7 +87,7 @@ export const DashboardListing = ({
|
|||
};
|
||||
}, [title, savedObjectsClient, redirectTo, data.query, kbnUrlStateStorage]);
|
||||
|
||||
const hideWriteControls = dashboardCapabilities.hideWriteControls;
|
||||
const { showWriteControls } = dashboardCapabilities;
|
||||
const listingLimit = savedObjects.settings.getListingLimit();
|
||||
const defaultFilter = title ? `"${title}"` : '';
|
||||
|
||||
|
@ -118,8 +118,8 @@ export const DashboardListing = ({
|
|||
}, [dashboardSessionStorage, redirectTo, core.overlays]);
|
||||
|
||||
const emptyPrompt = useMemo(
|
||||
() => getNoItemsMessage(hideWriteControls, core.application, createItem),
|
||||
[createItem, core.application, hideWriteControls]
|
||||
() => getNoItemsMessage(showWriteControls, core.application, createItem),
|
||||
[createItem, core.application, showWriteControls]
|
||||
);
|
||||
|
||||
const fetchItems = useCallback(
|
||||
|
@ -171,10 +171,10 @@ export const DashboardListing = ({
|
|||
} = dashboardListingTable;
|
||||
return (
|
||||
<TableListView
|
||||
createItem={hideWriteControls ? undefined : createItem}
|
||||
deleteItems={hideWriteControls ? undefined : deleteItems}
|
||||
createItem={!showWriteControls ? undefined : createItem}
|
||||
deleteItems={!showWriteControls ? undefined : deleteItems}
|
||||
initialPageSize={savedObjects.settings.getPerPage()}
|
||||
editItem={hideWriteControls ? undefined : editItem}
|
||||
editItem={!showWriteControls ? undefined : editItem}
|
||||
initialFilter={initialFilter ?? defaultFilter}
|
||||
toastNotifications={core.notifications.toasts}
|
||||
headingId="dashboardListingHeading"
|
||||
|
@ -239,11 +239,11 @@ const getTableColumns = (
|
|||
};
|
||||
|
||||
const getNoItemsMessage = (
|
||||
hideWriteControls: boolean,
|
||||
showWriteControls: boolean,
|
||||
application: ApplicationStart,
|
||||
createItem: () => void
|
||||
) => {
|
||||
if (hideWriteControls) {
|
||||
if (!showWriteControls) {
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
iconType="glasses"
|
||||
|
|
|
@ -62,7 +62,7 @@ export function makeDefaultServices(): DashboardAppServices {
|
|||
createNew: true,
|
||||
saveQuery: true,
|
||||
createShortUrl: true,
|
||||
hideWriteControls: false,
|
||||
showWriteControls: true,
|
||||
storeSearchSession: true,
|
||||
mapsCapabilities: { save: true },
|
||||
visualizeCapabilities: { save: true },
|
||||
|
|
|
@ -478,7 +478,7 @@ export function DashboardTopNav({
|
|||
dashboardAppState.getLatestDashboardState().viewMode,
|
||||
dashboardTopNavActions,
|
||||
{
|
||||
hideWriteControls: dashboardCapabilities.hideWriteControls,
|
||||
showWriteControls: dashboardCapabilities.showWriteControls,
|
||||
isDirty: Boolean(dashboardAppState.hasUnsavedChanges),
|
||||
isSaveInProgress: state.isSaveInProgress,
|
||||
isNewDashboard: !savedDashboard.id,
|
||||
|
|
|
@ -15,14 +15,14 @@ import { TopNavMenuData } from '../../../../navigation/public';
|
|||
/**
|
||||
* @param actions - A mapping of TopNavIds to an action function that should run when the
|
||||
* corresponding top nav is clicked.
|
||||
* @param hideWriteControls if true, does not include any controls that allow editing or creating objects.
|
||||
* @param showWriteControls if false, does not include any controls that allow editing or creating objects.
|
||||
* @return an array of objects for a top nav configuration, based on the mode.
|
||||
*/
|
||||
export function getTopNavConfig(
|
||||
dashboardMode: ViewMode,
|
||||
actions: { [key: string]: NavAction },
|
||||
options: {
|
||||
hideWriteControls: boolean;
|
||||
showWriteControls: boolean;
|
||||
isNewDashboard: boolean;
|
||||
isDirty: boolean;
|
||||
isSaveInProgress?: boolean;
|
||||
|
@ -32,7 +32,7 @@ export function getTopNavConfig(
|
|||
const labs = options.isLabsEnabled ? [getLabsConfig(actions[TopNavIds.LABS])] : [];
|
||||
switch (dashboardMode) {
|
||||
case ViewMode.VIEW:
|
||||
return options.hideWriteControls
|
||||
return !options.showWriteControls
|
||||
? [
|
||||
...labs,
|
||||
getFullScreenConfig(actions[TopNavIds.FULL_SCREEN]),
|
||||
|
|
|
@ -188,7 +188,7 @@ export class DashboardPlugin
|
|||
};
|
||||
return {
|
||||
SavedObjectFinder: getSavedObjectFinder(coreStart.savedObjects, coreStart.uiSettings),
|
||||
hideWriteControls: deps.kibanaLegacy.dashboardConfig.getHideWriteControls(),
|
||||
showWriteControls: Boolean(coreStart.application.capabilities.dashboard.showWriteControls),
|
||||
notifications: coreStart.notifications,
|
||||
application: coreStart.application,
|
||||
uiSettings: coreStart.uiSettings,
|
||||
|
|
|
@ -168,7 +168,7 @@ export interface DashboardAppCapabilities {
|
|||
createNew: boolean;
|
||||
saveQuery: boolean;
|
||||
createShortUrl: boolean;
|
||||
hideWriteControls: boolean;
|
||||
showWriteControls: boolean;
|
||||
storeSearchSession: boolean;
|
||||
mapsCapabilities: { save: boolean };
|
||||
visualizeCapabilities: { save: boolean };
|
||||
|
|
|
@ -1,29 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export interface DashboardConfig {
|
||||
turnHideWriteControlsOn(): void;
|
||||
getHideWriteControls(): boolean;
|
||||
}
|
||||
|
||||
export function getDashboardConfig(hideWriteControls: boolean): DashboardConfig {
|
||||
let _hideWriteControls = hideWriteControls;
|
||||
|
||||
return {
|
||||
/**
|
||||
* Part of the exposed plugin API - do not remove without careful consideration.
|
||||
* @type {boolean}
|
||||
*/
|
||||
turnHideWriteControlsOn() {
|
||||
_hideWriteControls = true;
|
||||
},
|
||||
getHideWriteControls() {
|
||||
return _hideWriteControls;
|
||||
},
|
||||
};
|
||||
}
|
|
@ -17,10 +17,6 @@ const createStartContract = (): Start => ({
|
|||
config: {
|
||||
defaultAppId: 'home',
|
||||
},
|
||||
dashboardConfig: {
|
||||
turnHideWriteControlsOn: jest.fn(),
|
||||
getHideWriteControls: jest.fn(),
|
||||
},
|
||||
loadFontAwesome: jest.fn(),
|
||||
loadAngularBootstrap: jest.fn(),
|
||||
});
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
import { PluginInitializerContext, CoreStart, CoreSetup } from 'kibana/public';
|
||||
import { ConfigSchema } from '../config';
|
||||
import { getDashboardConfig } from './dashboard_config';
|
||||
import { injectHeaderStyle } from './utils/inject_header_style';
|
||||
|
||||
export class KibanaLegacyPlugin {
|
||||
|
@ -21,11 +20,6 @@ export class KibanaLegacyPlugin {
|
|||
public start({ application, http: { basePath }, uiSettings }: CoreStart) {
|
||||
injectHeaderStyle(uiSettings);
|
||||
return {
|
||||
/**
|
||||
* Used to power dashboard mode. Should be removed when dashboard mode is removed eventually.
|
||||
* @deprecated
|
||||
*/
|
||||
dashboardConfig: getDashboardConfig(!application.capabilities.dashboard.showWriteControls),
|
||||
/**
|
||||
* Loads the font-awesome icon font. Should be removed once the last consumer has migrated to EUI
|
||||
* @deprecated
|
||||
|
|
|
@ -134,7 +134,6 @@ export const applicationUsageSchema = {
|
|||
// X-Pack
|
||||
apm: commonSchema,
|
||||
canvas: commonSchema,
|
||||
dashboard_mode: commonSchema, // It's a forward app so we'll likely never report it
|
||||
enterpriseSearch: commonSchema,
|
||||
appSearch: commonSchema,
|
||||
workplaceSearch: commonSchema,
|
||||
|
|
|
@ -35,7 +35,6 @@ const uiMetricFromDataPluginSchema: MakeSchemaFrom<UIMetricUsage> = {
|
|||
apm: commonSchema,
|
||||
csm: commonSchema,
|
||||
canvas: commonSchema,
|
||||
dashboard_mode: commonSchema, // It's a forward app so we'll likely never report it
|
||||
enterpriseSearch: commonSchema,
|
||||
appSearch: commonSchema,
|
||||
workplaceSearch: commonSchema,
|
||||
|
|
|
@ -2136,137 +2136,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"dashboard_mode": {
|
||||
"properties": {
|
||||
"appId": {
|
||||
"type": "keyword",
|
||||
"_meta": {
|
||||
"description": "The application being tracked"
|
||||
}
|
||||
},
|
||||
"viewId": {
|
||||
"type": "keyword",
|
||||
"_meta": {
|
||||
"description": "Always `main`"
|
||||
}
|
||||
},
|
||||
"clicks_total": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the application since we started counting them"
|
||||
}
|
||||
},
|
||||
"clicks_7_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the application over the last 7 days"
|
||||
}
|
||||
},
|
||||
"clicks_30_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the application over the last 30 days"
|
||||
}
|
||||
},
|
||||
"clicks_90_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the application over the last 90 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_total": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen since we started counting them."
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_7_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen over the last 7 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_30_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen over the last 30 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_90_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen over the last 90 days"
|
||||
}
|
||||
},
|
||||
"views": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"properties": {
|
||||
"appId": {
|
||||
"type": "keyword",
|
||||
"_meta": {
|
||||
"description": "The application being tracked"
|
||||
}
|
||||
},
|
||||
"viewId": {
|
||||
"type": "keyword",
|
||||
"_meta": {
|
||||
"description": "The application view being tracked"
|
||||
}
|
||||
},
|
||||
"clicks_total": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the application sub view since we started counting them"
|
||||
}
|
||||
},
|
||||
"clicks_7_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the active application sub view over the last 7 days"
|
||||
}
|
||||
},
|
||||
"clicks_30_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the active application sub view over the last 30 days"
|
||||
}
|
||||
},
|
||||
"clicks_90_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the active application sub view over the last 90 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_total": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application sub view is active and on-screen since we started counting them."
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_7_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen active application sub view over the last 7 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_30_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen active application sub view over the last 30 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_90_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen active application sub view over the last 90 days"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"enterpriseSearch": {
|
||||
"properties": {
|
||||
"appId": {
|
||||
|
@ -8487,25 +8356,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"dashboard_mode": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "keyword",
|
||||
"_meta": {
|
||||
"description": "The event that is tracked"
|
||||
}
|
||||
},
|
||||
"value": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "The value of the event"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"enterpriseSearch": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
"xpack.dashboard": "plugins/dashboard_enhanced",
|
||||
"xpack.discover": "plugins/discover_enhanced",
|
||||
"xpack.crossClusterReplication": "plugins/cross_cluster_replication",
|
||||
"xpack.dashboardMode": "plugins/dashboard_mode",
|
||||
"xpack.data": "plugins/data_enhanced",
|
||||
"xpack.embeddableEnhanced": "plugins/embeddable_enhanced",
|
||||
"xpack.endpoint": "plugins/endpoint",
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
The deprecated dashboard only mode.
|
|
@ -1,10 +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.
|
||||
*/
|
||||
|
||||
export const UI_SETTINGS = {
|
||||
CONFIG_DASHBOARD_ONLY_MODE_ROLES: 'xpackDashboardMode:roles',
|
||||
};
|
|
@ -1,8 +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.
|
||||
*/
|
||||
|
||||
export { UI_SETTINGS } from './constants';
|
|
@ -1,12 +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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../..',
|
||||
roots: ['<rootDir>/x-pack/plugins/dashboard_mode'],
|
||||
};
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"id": "dashboardMode",
|
||||
"owner": {
|
||||
"name": "Kibana Presentation",
|
||||
"githubTeam": "kibana-presentation"
|
||||
},
|
||||
"version": "8.0.0",
|
||||
"kibanaVersion": "kibana",
|
||||
"configPath": ["xpack", "dashboard_mode"],
|
||||
"optionalPlugins": ["security"],
|
||||
"requiredPlugins": ["kibanaLegacy", "urlForwarding", "dashboard"],
|
||||
"server": true,
|
||||
"ui": true
|
||||
}
|
|
@ -1,8 +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.
|
||||
*/
|
||||
|
||||
export { plugin } from './plugin';
|
|
@ -1,74 +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 { trimStart } from 'lodash';
|
||||
import { CoreSetup } from 'kibana/public';
|
||||
import { KibanaLegacyStart } from '../../../../src/plugins/kibana_legacy/public';
|
||||
import { UrlForwardingStart } from '../../../../src/plugins/url_forwarding/public';
|
||||
import {
|
||||
createDashboardEditUrl,
|
||||
DashboardConstants,
|
||||
} from '../../../../src/plugins/dashboard/public';
|
||||
import { AppNavLinkStatus } from '../../../../src/core/public';
|
||||
|
||||
function defaultUrl(defaultAppId: string) {
|
||||
const isDashboardId = defaultAppId.startsWith(dashboardAppIdPrefix());
|
||||
return isDashboardId ? `/${defaultAppId}` : DashboardConstants.LANDING_PAGE_PATH;
|
||||
}
|
||||
|
||||
function dashboardAppIdPrefix() {
|
||||
return trimStart(createDashboardEditUrl(''), '/');
|
||||
}
|
||||
|
||||
function migratePath(
|
||||
currentHash: string,
|
||||
kibanaLegacy: KibanaLegacyStart,
|
||||
urlForwarding: UrlForwardingStart
|
||||
) {
|
||||
if (currentHash === '' || currentHash === '#' || currentHash === '#/') {
|
||||
return `#${defaultUrl(kibanaLegacy.config.defaultAppId || '')}`;
|
||||
}
|
||||
if (!currentHash.startsWith('#/dashboard')) {
|
||||
return currentHash;
|
||||
}
|
||||
|
||||
const forwards = urlForwarding.getForwards();
|
||||
|
||||
if (currentHash.startsWith('#/dashboards')) {
|
||||
const { rewritePath: migrateListingPath } = forwards.find(
|
||||
({ legacyAppId }) => legacyAppId === 'dashboards'
|
||||
)!;
|
||||
return migrateListingPath(currentHash);
|
||||
}
|
||||
|
||||
const { rewritePath: migrateDetailPath } = forwards.find(
|
||||
({ legacyAppId }) => legacyAppId === 'dashboard'
|
||||
)!;
|
||||
return migrateDetailPath(currentHash);
|
||||
}
|
||||
|
||||
export const plugin = () => ({
|
||||
setup(core: CoreSetup<{ kibanaLegacy: KibanaLegacyStart; urlForwarding: UrlForwardingStart }>) {
|
||||
core.application.register({
|
||||
id: 'dashboard_mode',
|
||||
title: 'Dashboard mode',
|
||||
navLinkStatus: AppNavLinkStatus.hidden,
|
||||
mount: async () => {
|
||||
const [coreStart, { kibanaLegacy, urlForwarding }] = await core.getStartServices();
|
||||
kibanaLegacy.dashboardConfig.turnHideWriteControlsOn();
|
||||
coreStart.chrome.navLinks.showOnly('dashboards');
|
||||
setTimeout(() => {
|
||||
coreStart.application.navigateToApp('dashboards', {
|
||||
path: migratePath(window.location.hash, kibanaLegacy, urlForwarding),
|
||||
});
|
||||
}, 0);
|
||||
return () => {};
|
||||
},
|
||||
});
|
||||
},
|
||||
start() {},
|
||||
});
|
|
@ -1,23 +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 { PluginConfigDescriptor, PluginInitializerContext } from 'kibana/server';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
import { DashboardModeServerPlugin } from './plugin';
|
||||
|
||||
export const config: PluginConfigDescriptor = {
|
||||
schema: schema.object({
|
||||
enabled: schema.boolean({ defaultValue: true }),
|
||||
}),
|
||||
};
|
||||
|
||||
export function plugin(initializerContext: PluginInitializerContext) {
|
||||
return new DashboardModeServerPlugin(initializerContext);
|
||||
}
|
||||
|
||||
export { DashboardModeServerPlugin as Plugin };
|
|
@ -1,112 +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 { parse as parseUrl } from 'url';
|
||||
import {
|
||||
OnPostAuthHandler,
|
||||
OnPostAuthToolkit,
|
||||
KibanaRequest,
|
||||
LifecycleResponseFactory,
|
||||
IUiSettingsClient,
|
||||
} from 'kibana/server';
|
||||
import { coreMock } from '../../../../../src/core/server/mocks';
|
||||
|
||||
import { AuthenticatedUser } from '../../../security/server';
|
||||
import { securityMock } from '../../../security/server/mocks';
|
||||
|
||||
import { setupDashboardModeRequestInterceptor } from './dashboard_mode_request_interceptor';
|
||||
|
||||
const DASHBOARD_ONLY_MODE_ROLE = 'test_dashboard_only_mode_role';
|
||||
|
||||
describe('DashboardOnlyModeRequestInterceptor', () => {
|
||||
const core = coreMock.createSetup();
|
||||
const security = securityMock.createSetup();
|
||||
|
||||
let interceptor: OnPostAuthHandler;
|
||||
let toolkit: OnPostAuthToolkit;
|
||||
let uiSettingsMock: any;
|
||||
|
||||
beforeEach(() => {
|
||||
toolkit = {
|
||||
next: jest.fn(),
|
||||
};
|
||||
interceptor = setupDashboardModeRequestInterceptor({
|
||||
http: core.http,
|
||||
security,
|
||||
getUiSettingsClient: () =>
|
||||
(Promise.resolve({
|
||||
get: () => Promise.resolve(uiSettingsMock),
|
||||
}) as unknown) as Promise<IUiSettingsClient>,
|
||||
});
|
||||
});
|
||||
|
||||
test('should not redirects for not app/* requests', async () => {
|
||||
const request = ({
|
||||
url: {
|
||||
pathname: 'api/test',
|
||||
},
|
||||
} as unknown) as KibanaRequest;
|
||||
|
||||
interceptor(request, {} as LifecycleResponseFactory, toolkit);
|
||||
|
||||
expect(toolkit.next).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should not redirects not authenticated users', async () => {
|
||||
const request = ({
|
||||
url: {
|
||||
pathname: '/app/home',
|
||||
},
|
||||
} as unknown) as KibanaRequest;
|
||||
|
||||
interceptor(request, {} as LifecycleResponseFactory, toolkit);
|
||||
|
||||
expect(toolkit.next).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('request for dashboard-only user', () => {
|
||||
function testRedirectToDashboardModeApp(url: string) {
|
||||
describe(`requests to url:"${url}"`, () => {
|
||||
test('redirects to the dashboard_mode app instead', async () => {
|
||||
const { pathname, search, hash } = parseUrl(url);
|
||||
const request = ({
|
||||
url: { pathname, search, hash },
|
||||
credentials: {
|
||||
roles: [DASHBOARD_ONLY_MODE_ROLE],
|
||||
},
|
||||
} as unknown) as KibanaRequest;
|
||||
|
||||
const response = ({
|
||||
redirected: jest.fn(),
|
||||
} as unknown) as LifecycleResponseFactory;
|
||||
|
||||
security.authc.getCurrentUser = jest.fn(
|
||||
(r: KibanaRequest) =>
|
||||
(({
|
||||
roles: [DASHBOARD_ONLY_MODE_ROLE],
|
||||
} as unknown) as AuthenticatedUser)
|
||||
);
|
||||
|
||||
uiSettingsMock = [DASHBOARD_ONLY_MODE_ROLE];
|
||||
|
||||
await interceptor(request, response, toolkit);
|
||||
|
||||
expect(response.redirected).toHaveBeenCalledWith({
|
||||
headers: { location: `/mock-server-basepath/app/dashboard_mode` },
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
testRedirectToDashboardModeApp('/app/kibana');
|
||||
testRedirectToDashboardModeApp('/app/kibana#/foo/bar');
|
||||
testRedirectToDashboardModeApp('/app/kibana/foo/bar');
|
||||
testRedirectToDashboardModeApp('/app/kibana?foo=bar');
|
||||
testRedirectToDashboardModeApp('/app/dashboards?foo=bar');
|
||||
testRedirectToDashboardModeApp('/app/home?foo=bar');
|
||||
});
|
||||
});
|
|
@ -1,80 +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 { HttpServiceSetup, OnPostAuthHandler, IUiSettingsClient } from 'kibana/server';
|
||||
import { SecurityPluginSetup } from '../../../security/server';
|
||||
import { UI_SETTINGS } from '../../common';
|
||||
|
||||
const superuserRole = 'superuser';
|
||||
|
||||
interface DashboardModeRequestInterceptorDependencies {
|
||||
http: HttpServiceSetup;
|
||||
security: SecurityPluginSetup;
|
||||
getUiSettingsClient: () => Promise<IUiSettingsClient>;
|
||||
}
|
||||
|
||||
export const setupDashboardModeRequestInterceptor = ({
|
||||
http,
|
||||
security,
|
||||
getUiSettingsClient,
|
||||
}: DashboardModeRequestInterceptorDependencies) =>
|
||||
(async (request, response, toolkit) => {
|
||||
const path = request.url.pathname;
|
||||
const isAppRequest = path.startsWith('/app/');
|
||||
|
||||
if (!isAppRequest) {
|
||||
return toolkit.next();
|
||||
}
|
||||
|
||||
const authenticatedUser = security.authc.getCurrentUser(request);
|
||||
const roles = authenticatedUser?.roles || [];
|
||||
|
||||
if (!authenticatedUser || roles.length === 0) {
|
||||
return toolkit.next();
|
||||
}
|
||||
|
||||
const uiSettings = await getUiSettingsClient();
|
||||
const dashboardOnlyModeRoles = await uiSettings.get(
|
||||
UI_SETTINGS.CONFIG_DASHBOARD_ONLY_MODE_ROLES
|
||||
);
|
||||
|
||||
if (!dashboardOnlyModeRoles) {
|
||||
return toolkit.next();
|
||||
}
|
||||
|
||||
const isDashboardOnlyModeUser = roles.find((role) => dashboardOnlyModeRoles.includes(role));
|
||||
const isSuperUser = roles.find((role) => role === superuserRole);
|
||||
|
||||
const enforceDashboardOnlyMode = isDashboardOnlyModeUser && !isSuperUser;
|
||||
|
||||
if (enforceDashboardOnlyMode) {
|
||||
if (
|
||||
path.startsWith('/app/home') ||
|
||||
path.startsWith('/app/kibana') ||
|
||||
path.startsWith('/app/dashboards')
|
||||
) {
|
||||
const dashBoardModeUrl = `${http.basePath.get(request)}/app/dashboard_mode`;
|
||||
// If the user is in "Dashboard only mode" they should only be allowed to see
|
||||
// the dashboard app and none others.
|
||||
|
||||
return response.redirected({
|
||||
headers: {
|
||||
location: dashBoardModeUrl,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (path.startsWith('/app/dashboard_mode')) {
|
||||
// let through requests to the dashboard_mode app
|
||||
return toolkit.next();
|
||||
}
|
||||
|
||||
return response.notFound();
|
||||
}
|
||||
|
||||
return toolkit.next();
|
||||
}) as OnPostAuthHandler;
|
|
@ -1,8 +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.
|
||||
*/
|
||||
|
||||
export { setupDashboardModeRequestInterceptor } from './dashboard_mode_request_interceptor';
|
|
@ -1,63 +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 {
|
||||
PluginInitializerContext,
|
||||
CoreSetup,
|
||||
CoreStart,
|
||||
Plugin,
|
||||
SavedObjectsClient,
|
||||
Logger,
|
||||
} from '../../../../src/core/server';
|
||||
|
||||
import { SecurityPluginSetup } from '../../security/server';
|
||||
import { setupDashboardModeRequestInterceptor } from './interceptors';
|
||||
|
||||
import { getUiSettings } from './ui_settings';
|
||||
|
||||
interface DashboardModeServerSetupDependencies {
|
||||
security?: SecurityPluginSetup;
|
||||
}
|
||||
|
||||
export class DashboardModeServerPlugin implements Plugin<void, void> {
|
||||
private initializerContext: PluginInitializerContext;
|
||||
private logger?: Logger;
|
||||
|
||||
constructor(initializerContext: PluginInitializerContext) {
|
||||
this.initializerContext = initializerContext;
|
||||
}
|
||||
|
||||
public setup(core: CoreSetup, { security }: DashboardModeServerSetupDependencies) {
|
||||
this.logger = this.initializerContext.logger.get();
|
||||
|
||||
core.uiSettings.register(getUiSettings());
|
||||
|
||||
const getUiSettingsClient = async () => {
|
||||
const [coreStart] = await core.getStartServices();
|
||||
const { savedObjects, uiSettings } = coreStart;
|
||||
const savedObjectsClient = new SavedObjectsClient(savedObjects.createInternalRepository());
|
||||
|
||||
return uiSettings.asScopedToClient(savedObjectsClient);
|
||||
};
|
||||
|
||||
if (security) {
|
||||
const dashboardModeRequestInterceptor = setupDashboardModeRequestInterceptor({
|
||||
http: core.http,
|
||||
security,
|
||||
getUiSettingsClient,
|
||||
});
|
||||
|
||||
core.http.registerOnPostAuth(dashboardModeRequestInterceptor);
|
||||
|
||||
this.logger.debug(`registered DashboardModeRequestInterceptor`);
|
||||
}
|
||||
}
|
||||
|
||||
public start(core: CoreStart) {}
|
||||
|
||||
public stop() {}
|
||||
}
|
|
@ -1,36 +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 { i18n } from '@kbn/i18n';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { UiSettingsParams } from 'kibana/server';
|
||||
import { UI_SETTINGS } from '../common';
|
||||
|
||||
const DASHBOARD_ONLY_USER_ROLE = 'kibana_dashboard_only_user';
|
||||
|
||||
export function getUiSettings(): Record<string, UiSettingsParams<unknown>> {
|
||||
return {
|
||||
[UI_SETTINGS.CONFIG_DASHBOARD_ONLY_MODE_ROLES]: {
|
||||
name: i18n.translate('xpack.dashboardMode.uiSettings.dashboardsOnlyRolesTitle', {
|
||||
defaultMessage: 'Dashboards only roles',
|
||||
}),
|
||||
description: i18n.translate('xpack.dashboardMode.uiSettings.dashboardsOnlyRolesDescription', {
|
||||
defaultMessage: 'Roles that belong to View Dashboards Only mode',
|
||||
}),
|
||||
value: [DASHBOARD_ONLY_USER_ROLE],
|
||||
category: ['dashboard'],
|
||||
sensitive: true,
|
||||
deprecation: {
|
||||
message: i18n.translate('xpack.dashboardMode.uiSettings.dashboardsOnlyRolesDeprecation', {
|
||||
defaultMessage: 'This setting is deprecated and will be removed in Kibana 8.0.',
|
||||
}),
|
||||
docLinksKey: 'dashboardSettings',
|
||||
},
|
||||
schema: schema.arrayOf(schema.string()),
|
||||
},
|
||||
};
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./target/types",
|
||||
"emitDeclarationOnly": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
},
|
||||
"include": [
|
||||
"common/**/*",
|
||||
"public/**/*",
|
||||
"server/**/*",
|
||||
"../../../typings/**/*"
|
||||
],
|
||||
"references": [
|
||||
{ "path": "../../../src/core/tsconfig.json" },
|
||||
{ "path": "../../../src/plugins/dashboard/tsconfig.json" },
|
||||
{ "path": "../../../src/plugins/kibana_legacy/tsconfig.json" },
|
||||
{ "path": "../../../src/plugins/url_forwarding/tsconfig.json" },
|
||||
{ "path": "../security/tsconfig.json" }
|
||||
]
|
||||
}
|
|
@ -9,7 +9,6 @@ import { i18n } from '@kbn/i18n';
|
|||
import { DiscoverStart } from '../../../../../../src/plugins/discover/public';
|
||||
import { ViewMode, IEmbeddable } from '../../../../../../src/plugins/embeddable/public';
|
||||
import { StartServicesGetter } from '../../../../../../src/plugins/kibana_utils/public';
|
||||
import { KibanaLegacyStart } from '../../../../../../src/plugins/kibana_legacy/public';
|
||||
import { CoreStart } from '../../../../../../src/core/public';
|
||||
import { KibanaLocation } from '../../../../../../src/plugins/share/public';
|
||||
import * as shared from './shared';
|
||||
|
@ -18,11 +17,6 @@ export const ACTION_EXPLORE_DATA = 'ACTION_EXPLORE_DATA';
|
|||
|
||||
export interface PluginDeps {
|
||||
discover: Pick<DiscoverStart, 'locator'>;
|
||||
kibanaLegacy?: {
|
||||
dashboardConfig: {
|
||||
getHideWriteControls: KibanaLegacyStart['dashboardConfig']['getHideWriteControls'];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface CoreDeps {
|
||||
|
@ -53,11 +47,6 @@ export abstract class AbstractExploreDataAction<Context extends { embeddable?: I
|
|||
|
||||
if (capabilities.discover && !capabilities.discover.show) return false;
|
||||
if (!plugins.discover.locator) return false;
|
||||
const isDashboardOnlyMode = !!this.params
|
||||
.start()
|
||||
.plugins.kibanaLegacy?.dashboardConfig.getHideWriteControls();
|
||||
if (isDashboardOnlyMode) return false;
|
||||
|
||||
if (!shared.hasExactlyOneIndexPattern(embeddable)) return false;
|
||||
if (embeddable.getInput().viewMode !== ViewMode.VIEW) return false;
|
||||
|
||||
|
|
|
@ -35,12 +35,10 @@ const setup = (
|
|||
useRangeEvent = false,
|
||||
timeFieldName,
|
||||
filters = [],
|
||||
dashboardOnlyMode = false,
|
||||
}: {
|
||||
useRangeEvent?: boolean;
|
||||
filters?: Filter[];
|
||||
timeFieldName?: string;
|
||||
dashboardOnlyMode?: boolean;
|
||||
} = { filters: [] }
|
||||
) => {
|
||||
const core = coreMock.createStart();
|
||||
|
@ -65,11 +63,6 @@ const setup = (
|
|||
discover: {
|
||||
locator,
|
||||
},
|
||||
kibanaLegacy: {
|
||||
dashboardConfig: {
|
||||
getHideWriteControls: () => dashboardOnlyMode,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const params: Params = {
|
||||
|
@ -193,13 +186,6 @@ describe('"Explore underlying data" panel action', () => {
|
|||
expect(isCompatible).toBe(false);
|
||||
});
|
||||
|
||||
test('return false for dashboard_only mode', async () => {
|
||||
const { action, context } = setup({ dashboardOnlyMode: true });
|
||||
const isCompatible = await action.isCompatible(context);
|
||||
|
||||
expect(isCompatible).toBe(false);
|
||||
});
|
||||
|
||||
test('returns false if Discover app is disabled', async () => {
|
||||
const { action, context, core } = setup();
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ afterEach(() => {
|
|||
i18nTranslateSpy.mockClear();
|
||||
});
|
||||
|
||||
const setup = ({ dashboardOnlyMode = false }: { dashboardOnlyMode?: boolean } = {}) => {
|
||||
const setup = () => {
|
||||
const core = coreMock.createStart();
|
||||
const locator: DiscoverAppLocator = {
|
||||
getLocation: jest.fn(() =>
|
||||
|
@ -51,11 +51,6 @@ const setup = ({ dashboardOnlyMode = false }: { dashboardOnlyMode?: boolean } =
|
|||
discover: {
|
||||
locator,
|
||||
},
|
||||
kibanaLegacy: {
|
||||
dashboardConfig: {
|
||||
getHideWriteControls: () => dashboardOnlyMode,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const params: Params = {
|
||||
|
@ -177,13 +172,6 @@ describe('"Explore underlying data" panel action', () => {
|
|||
expect(isCompatible).toBe(false);
|
||||
});
|
||||
|
||||
test('return false for dashboard_only mode', async () => {
|
||||
const { action, context } = setup({ dashboardOnlyMode: true });
|
||||
const isCompatible = await action.isCompatible(context);
|
||||
|
||||
expect(isCompatible).toBe(false);
|
||||
});
|
||||
|
||||
test('returns false if Discover app is disabled', async () => {
|
||||
const { action, context, core } = setup();
|
||||
|
||||
|
|
|
@ -7609,9 +7609,6 @@
|
|||
"xpack.dashboard.drilldown.goToDashboard": "ダッシュボードに移動",
|
||||
"xpack.dashboard.FlyoutCreateDrilldownAction.displayName": "ドリルダウンを作成",
|
||||
"xpack.dashboard.panel.openFlyoutEditDrilldown.displayName": "ドリルダウンを管理",
|
||||
"xpack.dashboardMode.uiSettings.dashboardsOnlyRolesDeprecation": "この設定はサポートが終了し、Kibana 8.0 では削除されます。",
|
||||
"xpack.dashboardMode.uiSettings.dashboardsOnlyRolesDescription": "ダッシュボード表示専用モードのロールです",
|
||||
"xpack.dashboardMode.uiSettings.dashboardsOnlyRolesTitle": "ダッシュボード専用ロール",
|
||||
"xpack.data.mgmt.searchSessions.actionDelete": "削除",
|
||||
"xpack.data.mgmt.searchSessions.actionExtend": "延長",
|
||||
"xpack.data.mgmt.searchSessions.actionRename": "名前を編集",
|
||||
|
|
|
@ -7851,9 +7851,6 @@
|
|||
"xpack.dashboard.drilldown.goToDashboard": "前往仪表板",
|
||||
"xpack.dashboard.FlyoutCreateDrilldownAction.displayName": "创建向下钻取",
|
||||
"xpack.dashboard.panel.openFlyoutEditDrilldown.displayName": "管理向下钻取",
|
||||
"xpack.dashboardMode.uiSettings.dashboardsOnlyRolesDeprecation": "此设置已过时,将在 Kibana 8.0 中移除。",
|
||||
"xpack.dashboardMode.uiSettings.dashboardsOnlyRolesDescription": "属于“仅查看仪表板”模式的角色",
|
||||
"xpack.dashboardMode.uiSettings.dashboardsOnlyRolesTitle": "仅限仪表板的角色",
|
||||
"xpack.data.mgmt.searchSessions.actionDelete": "删除",
|
||||
"xpack.data.mgmt.searchSessions.actionExtend": "延长",
|
||||
"xpack.data.mgmt.searchSessions.actionRename": "编辑名称",
|
||||
|
|
|
@ -1,92 +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 expect from '@kbn/expect';
|
||||
|
||||
export default function ({ getPageObjects, getService }) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const dashboardPanelActions = getService('dashboardPanelActions');
|
||||
const PageObjects = getPageObjects(['common', 'dashboard', 'visualize', 'lens']);
|
||||
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/102366
|
||||
describe.skip('empty dashboard', function () {
|
||||
before(async () => {
|
||||
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional');
|
||||
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/lens/basic');
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
await PageObjects.dashboard.preserveCrossAppState();
|
||||
await PageObjects.dashboard.clickNewDashboard();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await PageObjects.dashboard.gotoDashboardLandingPage();
|
||||
});
|
||||
|
||||
it('adds Lens visualization to empty dashboard', async () => {
|
||||
const title = 'Dashboard Test Lens';
|
||||
await PageObjects.lens.createAndAddLensFromDashboard({ title, redirectToOrigin: true });
|
||||
await PageObjects.dashboard.waitForRenderComplete();
|
||||
await testSubjects.exists(`embeddablePanelHeading-${title}`);
|
||||
});
|
||||
|
||||
it('redirects via save and return button after edit', async () => {
|
||||
await dashboardPanelActions.openContextMenu();
|
||||
await dashboardPanelActions.clickEdit();
|
||||
await PageObjects.lens.saveAndReturn();
|
||||
});
|
||||
|
||||
it('redirects via save as button after edit, renaming itself', async () => {
|
||||
const newTitle = 'wowee, looks like I have a new title';
|
||||
const originalPanelCount = await PageObjects.dashboard.getPanelCount();
|
||||
await PageObjects.dashboard.waitForRenderComplete();
|
||||
await dashboardPanelActions.openContextMenu();
|
||||
await dashboardPanelActions.clickEdit();
|
||||
await PageObjects.lens.save(newTitle, false, true);
|
||||
await PageObjects.dashboard.waitForRenderComplete();
|
||||
const newPanelCount = await PageObjects.dashboard.getPanelCount();
|
||||
expect(newPanelCount).to.eql(originalPanelCount);
|
||||
const titles = await PageObjects.dashboard.getPanelTitles();
|
||||
expect(titles.indexOf(newTitle)).to.not.be(-1);
|
||||
});
|
||||
|
||||
it('redirects via save as button after edit, adding a new panel', async () => {
|
||||
const newTitle = 'wowee, my title just got cooler';
|
||||
const originalPanelCount = await PageObjects.dashboard.getPanelCount();
|
||||
await PageObjects.dashboard.waitForRenderComplete();
|
||||
await dashboardPanelActions.openContextMenu();
|
||||
await dashboardPanelActions.clickEdit();
|
||||
await PageObjects.lens.save(newTitle, true, true);
|
||||
await PageObjects.dashboard.waitForRenderComplete();
|
||||
const newPanelCount = await PageObjects.dashboard.getPanelCount();
|
||||
expect(newPanelCount).to.eql(originalPanelCount + 1);
|
||||
const titles = await PageObjects.dashboard.getPanelTitles();
|
||||
expect(titles.indexOf(newTitle)).to.not.be(-1);
|
||||
});
|
||||
|
||||
it('loses originatingApp connection after save as when redirectToOrigin is false', async () => {
|
||||
await PageObjects.dashboard.saveDashboard('empty dashboard test');
|
||||
await PageObjects.dashboard.switchToEditMode();
|
||||
const newTitle = 'wowee, my title just got cooler again';
|
||||
await PageObjects.dashboard.waitForRenderComplete();
|
||||
await dashboardPanelActions.openContextMenu();
|
||||
await dashboardPanelActions.clickEdit();
|
||||
await PageObjects.lens.save(newTitle, true, false);
|
||||
await PageObjects.lens.notLinkedToOriginatingApp();
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
});
|
||||
|
||||
it('loses originatingApp connection after first save when redirectToOrigin is false', async () => {
|
||||
const title = 'non-dashboard Test Lens';
|
||||
await PageObjects.dashboard.loadSavedDashboard('empty dashboard test');
|
||||
await PageObjects.dashboard.switchToEditMode();
|
||||
await PageObjects.lens.createAndAddLensFromDashboard({ title });
|
||||
await PageObjects.lens.notLinkedToOriginatingApp();
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,173 +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 expect from '@kbn/expect';
|
||||
|
||||
export default function ({ getService, getPageObjects }) {
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const browser = getService('browser');
|
||||
const log = getService('log');
|
||||
const pieChart = getService('pieChart');
|
||||
const security = getService('security');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const dashboardPanelActions = getService('dashboardPanelActions');
|
||||
const appsMenu = getService('appsMenu');
|
||||
const filterBar = getService('filterBar');
|
||||
const PageObjects = getPageObjects([
|
||||
'security',
|
||||
'common',
|
||||
'discover',
|
||||
'dashboard',
|
||||
'header',
|
||||
'settings',
|
||||
'timePicker',
|
||||
'share',
|
||||
]);
|
||||
const dashboardName = 'Dashboard View Mode Test Dashboard';
|
||||
|
||||
describe('Dashboard View Mode', function () {
|
||||
this.tags(['skipFirefox']);
|
||||
|
||||
before('initialize tests', async () => {
|
||||
log.debug('Dashboard View Mode:initTests');
|
||||
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional');
|
||||
await kibanaServer.importExport.load(
|
||||
'x-pack/test/functional/fixtures/kbn_archiver/dashboard_view_mode'
|
||||
);
|
||||
await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' });
|
||||
await browser.setWindowSize(1600, 1000);
|
||||
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await kibanaServer.importExport.unload(
|
||||
'x-pack/test/functional/fixtures/kbn_archiver/dashboard_view_mode'
|
||||
);
|
||||
const types = [
|
||||
'search',
|
||||
'dashboard',
|
||||
'visualization',
|
||||
'search-session',
|
||||
'core-usage-stats',
|
||||
'event_loop_delays_daily',
|
||||
'search-telemetry',
|
||||
'core-usage-stats',
|
||||
];
|
||||
await kibanaServer.savedObjects.clean({ types });
|
||||
});
|
||||
|
||||
// FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/109351
|
||||
describe.skip('Dashboard viewer', () => {
|
||||
after(async () => {
|
||||
await security.testUser.restoreDefaults();
|
||||
});
|
||||
|
||||
it('shows only the dashboard app link', async () => {
|
||||
await security.testUser.setRoles(['test_logstash_reader', 'kibana_dashboard_only_user']);
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
const appLinks = await appsMenu.readLinks();
|
||||
expect(appLinks).to.have.length(1);
|
||||
expect(appLinks[0]).to.have.property('text', 'Dashboard');
|
||||
});
|
||||
|
||||
it('shows the dashboard landing page by default', async () => {
|
||||
const currentUrl = await browser.getCurrentUrl();
|
||||
console.log('url: ', currentUrl);
|
||||
expect(currentUrl).to.contain('dashboards');
|
||||
});
|
||||
|
||||
it('does not show the create dashboard button', async () => {
|
||||
const createNewButtonExists = await testSubjects.exists('newItemButton');
|
||||
expect(createNewButtonExists).to.be(false);
|
||||
});
|
||||
|
||||
it('opens a dashboard up', async () => {
|
||||
await PageObjects.dashboard.loadSavedDashboard(dashboardName);
|
||||
const onDashboardLandingPage = await PageObjects.dashboard.onDashboardLandingPage();
|
||||
expect(onDashboardLandingPage).to.be(false);
|
||||
});
|
||||
|
||||
it('can filter on a visualization', async () => {
|
||||
await PageObjects.timePicker.setHistoricalDataRange();
|
||||
await pieChart.filterOnPieSlice();
|
||||
const filterCount = await filterBar.getFilterCount();
|
||||
expect(filterCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('shows the full screen menu item', async () => {
|
||||
const fullScreenMenuItemExists = await testSubjects.exists('dashboardFullScreenMode');
|
||||
expect(fullScreenMenuItemExists).to.be(true);
|
||||
});
|
||||
|
||||
it('does not show the edit menu item', async () => {
|
||||
const editMenuItemExists = await testSubjects.exists('dashboardEditMode');
|
||||
expect(editMenuItemExists).to.be(false);
|
||||
});
|
||||
|
||||
it('does not show the view menu item', async () => {
|
||||
const viewMenuItemExists = await testSubjects.exists('dashboardViewOnlyMode');
|
||||
expect(viewMenuItemExists).to.be(false);
|
||||
});
|
||||
|
||||
it('does not show the reporting menu item', async () => {
|
||||
const reportingMenuItemExists = await testSubjects.exists('topNavReportingLink');
|
||||
expect(reportingMenuItemExists).to.be(false);
|
||||
});
|
||||
|
||||
it('shows the sharing menu item', async () => {
|
||||
const shareMenuItemExists = await testSubjects.exists('shareTopNavButton');
|
||||
expect(shareMenuItemExists).to.be(true);
|
||||
});
|
||||
|
||||
it(`Permalinks doesn't show create short-url button`, async () => {
|
||||
await PageObjects.share.openShareMenuItem('Permalinks');
|
||||
await PageObjects.share.createShortUrlMissingOrFail();
|
||||
});
|
||||
|
||||
it('does not show the visualization edit icon', async () => {
|
||||
await dashboardPanelActions.expectMissingEditPanelAction();
|
||||
});
|
||||
|
||||
it('does not show the visualization delete icon', async () => {
|
||||
await dashboardPanelActions.expectMissingRemovePanelAction();
|
||||
});
|
||||
|
||||
it('shows the timepicker', async () => {
|
||||
const timePickerExists = await PageObjects.timePicker.timePickerExists();
|
||||
expect(timePickerExists).to.be(true);
|
||||
});
|
||||
|
||||
it('can paginate on a saved search', async () => {
|
||||
await PageObjects.dashboard.expectToolbarPaginationDisplayed({ displayed: true });
|
||||
});
|
||||
|
||||
it('is loaded for a user who is assigned a non-dashboard mode role', async () => {
|
||||
await security.testUser.setRoles([
|
||||
'test_logstash_reader',
|
||||
'kibana_dashboard_only_user',
|
||||
'kibana_admin',
|
||||
]);
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
||||
if (await appsMenu.linkExists('Stack Management')) {
|
||||
throw new Error('Expected management nav link to not be shown');
|
||||
}
|
||||
});
|
||||
|
||||
it('is not loaded for a user who is assigned a superuser role', async () => {
|
||||
await security.testUser.setRoles(['kibana_dashboard_only_user', 'superuser']);
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
||||
if (!(await appsMenu.linkExists('Stack Management'))) {
|
||||
throw new Error('Expected management nav link to be shown');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
||||
export default function ({ loadTestFile }) {
|
||||
describe('dashboard mode', function () {
|
||||
this.tags('ciGroup7');
|
||||
|
||||
loadTestFile(require.resolve('./dashboard_view_mode'));
|
||||
loadTestFile(require.resolve('./dashboard_empty_screen'));
|
||||
});
|
||||
}
|
|
@ -29,7 +29,6 @@ export default async function ({ readConfigFile }) {
|
|||
resolve(__dirname, './apps/monitoring'),
|
||||
resolve(__dirname, './apps/watcher'),
|
||||
resolve(__dirname, './apps/dashboard'),
|
||||
resolve(__dirname, './apps/dashboard_mode'),
|
||||
resolve(__dirname, './apps/discover'),
|
||||
resolve(__dirname, './apps/security'),
|
||||
resolve(__dirname, './apps/spaces'),
|
||||
|
|
|
@ -54,7 +54,6 @@
|
|||
{ "path": "../plugins/banners/tsconfig.json" },
|
||||
{ "path": "../plugins/cases/tsconfig.json" },
|
||||
{ "path": "../plugins/cloud/tsconfig.json" },
|
||||
{ "path": "../plugins/dashboard_mode/tsconfig.json" },
|
||||
{ "path": "../plugins/enterprise_search/tsconfig.json" },
|
||||
{ "path": "../plugins/fleet/tsconfig.json" },
|
||||
{ "path": "../plugins/global_search/tsconfig.json" },
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue