From 4971a2c772317ed8092ca8f35e539fe71b828b3b Mon Sep 17 00:00:00 2001
From: Mikhail Shustov
Date: Tue, 21 Jan 2020 12:58:40 +0100
Subject: [PATCH 001/187] Expose fatalErrors API from the Start contract
(#55300)
* Expose FatalErrors from the Start contract.
This API is intended to be used for runtime as well.
* update docs
* update data plugin snapshot to fix tests
* address comments
Co-authored-by: Elastic Machine
---
...ana-plugin-public.chromenavlinks.update.md | 60 +++++++++----------
...ana-plugin-public.corestart.fatalerrors.md | 13 ++++
.../public/kibana-plugin-public.corestart.md | 1 +
.../kibana-plugin-public.fatalerrorsstart.md | 13 ++++
.../core/public/kibana-plugin-public.md | 1 +
src/core/public/core_system.ts | 2 +
.../fatal_errors/fatal_errors_service.mock.ts | 4 ++
.../fatal_errors/fatal_errors_service.tsx | 23 ++++++-
src/core/public/fatal_errors/index.ts | 2 +-
src/core/public/index.ts | 5 +-
src/core/public/legacy/legacy_service.test.ts | 2 +
src/core/public/mocks.ts | 1 +
src/core/public/plugins/plugin_context.ts | 1 +
.../public/plugins/plugins_service.test.ts | 1 +
src/core/public/public.api.md | 5 ++
.../query_string_input.test.tsx.snap | 24 ++++++++
16 files changed, 123 insertions(+), 35 deletions(-)
create mode 100644 docs/development/core/public/kibana-plugin-public.corestart.fatalerrors.md
create mode 100644 docs/development/core/public/kibana-plugin-public.fatalerrorsstart.md
diff --git a/docs/development/core/public/kibana-plugin-public.chromenavlinks.update.md b/docs/development/core/public/kibana-plugin-public.chromenavlinks.update.md
index d1cd2d3b04950..155d149f334a1 100644
--- a/docs/development/core/public/kibana-plugin-public.chromenavlinks.update.md
+++ b/docs/development/core/public/kibana-plugin-public.chromenavlinks.update.md
@@ -1,30 +1,30 @@
-
-
-[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [ChromeNavLinks](./kibana-plugin-public.chromenavlinks.md) > [update](./kibana-plugin-public.chromenavlinks.update.md)
-
-## ChromeNavLinks.update() method
-
-> Warning: This API is now obsolete.
->
-> Uses the [AppBase.updater$](./kibana-plugin-public.appbase.updater_.md) property when registering your application with [ApplicationSetup.register()](./kibana-plugin-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.
-
-Signature:
-
-```typescript
-update(id: string, values: ChromeNavLinkUpdateableFields): ChromeNavLink | undefined;
-```
-
-## Parameters
-
-| Parameter | Type | Description |
-| --- | --- | --- |
-| id | string
| |
-| values | ChromeNavLinkUpdateableFields
| |
-
-Returns:
-
-`ChromeNavLink | undefined`
-
+
+
+[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [ChromeNavLinks](./kibana-plugin-public.chromenavlinks.md) > [update](./kibana-plugin-public.chromenavlinks.update.md)
+
+## ChromeNavLinks.update() method
+
+> Warning: This API is now obsolete.
+>
+> Uses the [AppBase.updater$](./kibana-plugin-public.appbase.updater_.md) property when registering your application with [ApplicationSetup.register()](./kibana-plugin-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.
+
+Signature:
+
+```typescript
+update(id: string, values: ChromeNavLinkUpdateableFields): ChromeNavLink | undefined;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| id | string
| |
+| values | ChromeNavLinkUpdateableFields
| |
+
+Returns:
+
+`ChromeNavLink | undefined`
+
diff --git a/docs/development/core/public/kibana-plugin-public.corestart.fatalerrors.md b/docs/development/core/public/kibana-plugin-public.corestart.fatalerrors.md
new file mode 100644
index 0000000000000..540b17b5a6f0b
--- /dev/null
+++ b/docs/development/core/public/kibana-plugin-public.corestart.fatalerrors.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [CoreStart](./kibana-plugin-public.corestart.md) > [fatalErrors](./kibana-plugin-public.corestart.fatalerrors.md)
+
+## CoreStart.fatalErrors property
+
+[FatalErrorsStart](./kibana-plugin-public.fatalerrorsstart.md)
+
+Signature:
+
+```typescript
+fatalErrors: FatalErrorsStart;
+```
diff --git a/docs/development/core/public/kibana-plugin-public.corestart.md b/docs/development/core/public/kibana-plugin-public.corestart.md
index e561ee313f100..83af82d590c36 100644
--- a/docs/development/core/public/kibana-plugin-public.corestart.md
+++ b/docs/development/core/public/kibana-plugin-public.corestart.md
@@ -19,6 +19,7 @@ export interface CoreStart
| [application](./kibana-plugin-public.corestart.application.md) | ApplicationStart
| [ApplicationStart](./kibana-plugin-public.applicationstart.md) |
| [chrome](./kibana-plugin-public.corestart.chrome.md) | ChromeStart
| [ChromeStart](./kibana-plugin-public.chromestart.md) |
| [docLinks](./kibana-plugin-public.corestart.doclinks.md) | DocLinksStart
| [DocLinksStart](./kibana-plugin-public.doclinksstart.md) |
+| [fatalErrors](./kibana-plugin-public.corestart.fatalerrors.md) | FatalErrorsStart
| [FatalErrorsStart](./kibana-plugin-public.fatalerrorsstart.md) |
| [http](./kibana-plugin-public.corestart.http.md) | HttpStart
| [HttpStart](./kibana-plugin-public.httpstart.md) |
| [i18n](./kibana-plugin-public.corestart.i18n.md) | I18nStart
| [I18nStart](./kibana-plugin-public.i18nstart.md) |
| [injectedMetadata](./kibana-plugin-public.corestart.injectedmetadata.md) | {
getInjectedVar: (name: string, defaultValue?: any) => unknown;
}
| exposed temporarily until https://github.com/elastic/kibana/issues/41990 done use \*only\* to retrieve config values. There is no way to set injected values in the new platform. Use the legacy platform API instead. |
diff --git a/docs/development/core/public/kibana-plugin-public.fatalerrorsstart.md b/docs/development/core/public/kibana-plugin-public.fatalerrorsstart.md
new file mode 100644
index 0000000000000..a8ece7dcb7e02
--- /dev/null
+++ b/docs/development/core/public/kibana-plugin-public.fatalerrorsstart.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [FatalErrorsStart](./kibana-plugin-public.fatalerrorsstart.md)
+
+## FatalErrorsStart type
+
+FatalErrors stop the Kibana Public Core and displays a fatal error screen with details about the Kibana build and the error.
+
+Signature:
+
+```typescript
+export declare type FatalErrorsStart = FatalErrorsSetup;
+```
diff --git a/docs/development/core/public/kibana-plugin-public.md b/docs/development/core/public/kibana-plugin-public.md
index 27ca9f2d9fd57..27037d46926c1 100644
--- a/docs/development/core/public/kibana-plugin-public.md
+++ b/docs/development/core/public/kibana-plugin-public.md
@@ -129,6 +129,7 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [ChromeHelpExtensionMenuGitHubLink](./kibana-plugin-public.chromehelpextensionmenugithublink.md) | |
| [ChromeHelpExtensionMenuLink](./kibana-plugin-public.chromehelpextensionmenulink.md) | |
| [ChromeNavLinkUpdateableFields](./kibana-plugin-public.chromenavlinkupdateablefields.md) | |
+| [FatalErrorsStart](./kibana-plugin-public.fatalerrorsstart.md) | FatalErrors stop the Kibana Public Core and displays a fatal error screen with details about the Kibana build and the error. |
| [HandlerContextType](./kibana-plugin-public.handlercontexttype.md) | Extracts the type of the first argument of a [HandlerFunction](./kibana-plugin-public.handlerfunction.md) to represent the type of the context. |
| [HandlerFunction](./kibana-plugin-public.handlerfunction.md) | A function that accepts a context object and an optional number of additional arguments. Used for the generic types in [IContextContainer](./kibana-plugin-public.icontextcontainer.md) |
| [HandlerParameters](./kibana-plugin-public.handlerparameters.md) | Extracts the types of the additional arguments of a [HandlerFunction](./kibana-plugin-public.handlerfunction.md), excluding the [HandlerContextType](./kibana-plugin-public.handlercontexttype.md). |
diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts
index 5b31c740518e4..97bd49416f705 100644
--- a/src/core/public/core_system.ts
+++ b/src/core/public/core_system.ts
@@ -214,6 +214,7 @@ export class CoreSystem {
const http = await this.http.start({ injectedMetadata, fatalErrors: this.fatalErrorsSetup! });
const savedObjects = await this.savedObjects.start({ http });
const i18n = await this.i18n.start();
+ const fatalErrors = await this.fatalErrors.start();
await this.integrations.start({ uiSettings });
const coreUiTargetDomElement = document.createElement('div');
@@ -271,6 +272,7 @@ export class CoreSystem {
notifications,
overlays,
uiSettings,
+ fatalErrors,
};
const plugins = await this.plugins.start(core);
diff --git a/src/core/public/fatal_errors/fatal_errors_service.mock.ts b/src/core/public/fatal_errors/fatal_errors_service.mock.ts
index dd7702a7ee7dd..6d9876f787fa9 100644
--- a/src/core/public/fatal_errors/fatal_errors_service.mock.ts
+++ b/src/core/public/fatal_errors/fatal_errors_service.mock.ts
@@ -26,18 +26,22 @@ const createSetupContractMock = () => {
return setupContract;
};
+const createStartContractMock = createSetupContractMock;
type FatalErrorsServiceContract = PublicMethodsOf;
const createMock = () => {
const mocked: jest.Mocked = {
setup: jest.fn(),
+ start: jest.fn(),
};
mocked.setup.mockReturnValue(createSetupContractMock());
+ mocked.start.mockReturnValue(createStartContractMock());
return mocked;
};
export const fatalErrorsServiceMock = {
create: createMock,
createSetupContract: createSetupContractMock,
+ createStartContract: createStartContractMock,
};
diff --git a/src/core/public/fatal_errors/fatal_errors_service.tsx b/src/core/public/fatal_errors/fatal_errors_service.tsx
index 5c6a7bb322ae1..309f07859ef26 100644
--- a/src/core/public/fatal_errors/fatal_errors_service.tsx
+++ b/src/core/public/fatal_errors/fatal_errors_service.tsx
@@ -54,9 +54,18 @@ export interface FatalErrorsSetup {
get$: () => Rx.Observable;
}
+/**
+ * FatalErrors stop the Kibana Public Core and displays a fatal error screen
+ * with details about the Kibana build and the error.
+ *
+ * @public
+ */
+export type FatalErrorsStart = FatalErrorsSetup;
+
/** @interal */
export class FatalErrorsService {
private readonly errorInfo$ = new Rx.ReplaySubject();
+ private fatalErrors?: FatalErrorsSetup;
/**
*
@@ -82,7 +91,7 @@ export class FatalErrorsService {
},
});
- const fatalErrorsSetup: FatalErrorsSetup = {
+ this.fatalErrors = {
add: (error, source?) => {
const errorInfo = getErrorInfo(error, source);
@@ -101,9 +110,17 @@ export class FatalErrorsService {
},
};
- this.setupGlobalErrorHandlers(fatalErrorsSetup);
+ this.setupGlobalErrorHandlers(this.fatalErrors!);
- return fatalErrorsSetup;
+ return this.fatalErrors!;
+ }
+
+ public start() {
+ const { fatalErrors } = this;
+ if (!fatalErrors) {
+ throw new Error('FatalErrorsService#setup() must be invoked before start.');
+ }
+ return fatalErrors;
}
private renderError(injectedMetadata: InjectedMetadataSetup, i18n: I18nStart) {
diff --git a/src/core/public/fatal_errors/index.ts b/src/core/public/fatal_errors/index.ts
index e37a36152cf91..c8ea1c0bccd22 100644
--- a/src/core/public/fatal_errors/index.ts
+++ b/src/core/public/fatal_errors/index.ts
@@ -17,5 +17,5 @@
* under the License.
*/
-export { FatalErrorsSetup, FatalErrorsService } from './fatal_errors_service';
+export { FatalErrorsSetup, FatalErrorsStart, FatalErrorsService } from './fatal_errors_service';
export { FatalErrorInfo } from './get_error_info';
diff --git a/src/core/public/index.ts b/src/core/public/index.ts
index 5b17eccc37f8b..5e732dd05e616 100644
--- a/src/core/public/index.ts
+++ b/src/core/public/index.ts
@@ -55,7 +55,7 @@ import {
ChromeRecentlyAccessed,
ChromeRecentlyAccessedHistoryItem,
} from './chrome';
-import { FatalErrorsSetup, FatalErrorInfo } from './fatal_errors';
+import { FatalErrorsSetup, FatalErrorsStart, FatalErrorInfo } from './fatal_errors';
import { HttpSetup, HttpStart } from './http';
import { I18nStart } from './i18n';
import { InjectedMetadataSetup, InjectedMetadataStart, LegacyNavLink } from './injected_metadata';
@@ -232,6 +232,8 @@ export interface CoreStart {
overlays: OverlayStart;
/** {@link IUiSettingsClient} */
uiSettings: IUiSettingsClient;
+ /** {@link FatalErrorsStart} */
+ fatalErrors: FatalErrorsStart;
/**
* exposed temporarily until https://github.com/elastic/kibana/issues/41990 done
* use *only* to retrieve config values. There is no way to set injected values
@@ -302,6 +304,7 @@ export {
DocLinksStart,
FatalErrorInfo,
FatalErrorsSetup,
+ FatalErrorsStart,
HttpSetup,
HttpStart,
I18nStart,
diff --git a/src/core/public/legacy/legacy_service.test.ts b/src/core/public/legacy/legacy_service.test.ts
index 9dd24f9e4a7a3..d08c8b52e39c9 100644
--- a/src/core/public/legacy/legacy_service.test.ts
+++ b/src/core/public/legacy/legacy_service.test.ts
@@ -98,6 +98,7 @@ const notificationsStart = notificationServiceMock.createStartContract();
const overlayStart = overlayServiceMock.createStartContract();
const uiSettingsStart = uiSettingsServiceMock.createStartContract();
const savedObjectsStart = savedObjectsMock.createStartContract();
+const fatalErrorsStart = fatalErrorsServiceMock.createStartContract();
const mockStorage = { getItem: jest.fn() } as any;
const defaultStartDeps = {
@@ -112,6 +113,7 @@ const defaultStartDeps = {
overlays: overlayStart,
uiSettings: uiSettingsStart,
savedObjects: savedObjectsStart,
+ fatalErrors: fatalErrorsStart,
},
lastSubUrlStorage: mockStorage,
targetDomElement: document.createElement('div'),
diff --git a/src/core/public/mocks.ts b/src/core/public/mocks.ts
index 43c8aa6f1d6b9..ce90d49065ad4 100644
--- a/src/core/public/mocks.ts
+++ b/src/core/public/mocks.ts
@@ -74,6 +74,7 @@ function createCoreStartMock({ basePath = '' } = {}) {
injectedMetadata: {
getInjectedVar: injectedMetadataServiceMock.createStartContract().getInjectedVar,
},
+ fatalErrors: fatalErrorsServiceMock.createStartContract(),
};
return mock;
diff --git a/src/core/public/plugins/plugin_context.ts b/src/core/public/plugins/plugin_context.ts
index f146c2452868b..48100cba4f26e 100644
--- a/src/core/public/plugins/plugin_context.ts
+++ b/src/core/public/plugins/plugin_context.ts
@@ -151,5 +151,6 @@ export function createPluginStartContext<
injectedMetadata: {
getInjectedVar: deps.injectedMetadata.getInjectedVar,
},
+ fatalErrors: deps.fatalErrors,
};
}
diff --git a/src/core/public/plugins/plugins_service.test.ts b/src/core/public/plugins/plugins_service.test.ts
index cafc7e5887e38..dbbcda8d60e12 100644
--- a/src/core/public/plugins/plugins_service.test.ts
+++ b/src/core/public/plugins/plugins_service.test.ts
@@ -111,6 +111,7 @@ describe('PluginsService', () => {
overlays: overlayServiceMock.createStartContract(),
uiSettings: uiSettingsServiceMock.createStartContract(),
savedObjects: savedObjectsMock.createStartContract(),
+ fatalErrors: fatalErrorsServiceMock.createStartContract(),
};
mockStartContext = {
...mockStartDeps,
diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md
index abd39e864bd30..f7b260c68ee96 100644
--- a/src/core/public/public.api.md
+++ b/src/core/public/public.api.md
@@ -377,6 +377,8 @@ export interface CoreStart {
// (undocumented)
docLinks: DocLinksStart;
// (undocumented)
+ fatalErrors: FatalErrorsStart;
+ // (undocumented)
http: HttpStart;
// (undocumented)
i18n: I18nStart;
@@ -532,6 +534,9 @@ export interface FatalErrorsSetup {
get$: () => Rx.Observable;
}
+// @public
+export type FatalErrorsStart = FatalErrorsSetup;
+
// @public
export type HandlerContextType> = T extends HandlerFunction ? U : never;
diff --git a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
index bc08c87304fca..d8db53d4c6020 100644
--- a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
+++ b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
@@ -304,6 +304,10 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA
},
},
},
+ "fatalErrors": Object {
+ "add": [MockFunction],
+ "get$": [MockFunction],
+ },
"http": Object {
"addLoadingCountSource": [MockFunction],
"anonymousPaths": Object {
@@ -930,6 +934,10 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA
},
},
},
+ "fatalErrors": Object {
+ "add": [MockFunction],
+ "get$": [MockFunction],
+ },
"http": Object {
"addLoadingCountSource": [MockFunction],
"anonymousPaths": Object {
@@ -1538,6 +1546,10 @@ exports[`QueryStringInput Should pass the query language to the language switche
},
},
},
+ "fatalErrors": Object {
+ "add": [MockFunction],
+ "get$": [MockFunction],
+ },
"http": Object {
"addLoadingCountSource": [MockFunction],
"anonymousPaths": Object {
@@ -2161,6 +2173,10 @@ exports[`QueryStringInput Should pass the query language to the language switche
},
},
},
+ "fatalErrors": Object {
+ "add": [MockFunction],
+ "get$": [MockFunction],
+ },
"http": Object {
"addLoadingCountSource": [MockFunction],
"anonymousPaths": Object {
@@ -2769,6 +2785,10 @@ exports[`QueryStringInput Should render the given query 1`] = `
},
},
},
+ "fatalErrors": Object {
+ "add": [MockFunction],
+ "get$": [MockFunction],
+ },
"http": Object {
"addLoadingCountSource": [MockFunction],
"anonymousPaths": Object {
@@ -3392,6 +3412,10 @@ exports[`QueryStringInput Should render the given query 1`] = `
},
},
},
+ "fatalErrors": Object {
+ "add": [MockFunction],
+ "get$": [MockFunction],
+ },
"http": Object {
"addLoadingCountSource": [MockFunction],
"anonymousPaths": Object {
From 27c8a4bc25badb3ee3c1feaa7e1e68b10bab1ccd Mon Sep 17 00:00:00 2001
From: Anton Dosov
Date: Tue, 21 Jan 2020 13:04:49 +0100
Subject: [PATCH 002/187] [State Management] remove AppState from Dashboard app
(#54105)
Removes AppState from dashboard app and replaces it with state containers and state syncing utilities.
---
.../filter_manager/filter_state_manager.ts | 14 +-
.../public/dashboard/__tests__/index.ts | 1 -
.../kibana/public/dashboard/legacy_imports.ts | 6 -
.../public/dashboard/np_ready/application.ts | 7 -
.../dashboard/np_ready/dashboard_app.tsx | 16 +-
.../np_ready/dashboard_app_controller.tsx | 110 ++++++---
.../np_ready/dashboard_state.test.ts | 13 +-
.../np_ready/dashboard_state_manager.ts | 233 ++++++++++++------
.../public/dashboard/np_ready/legacy_app.js | 12 +-
.../np_ready/lib/migrate_app_state.test.ts | 35 +--
.../np_ready/lib/migrate_app_state.ts | 15 +-
.../dashboard/np_ready/lib/save_dashboard.ts | 11 +-
.../np_ready/lib/update_saved_dashboard.ts | 4 +-
.../kibana/public/dashboard/np_ready/types.ts | 43 ++--
src/legacy/ui/public/chrome/api/controls.ts | 2 +
.../ui/public/chrome/directives/kbn_chrome.js | 10 +
.../ui/public/state_management/state.js | 2 +-
.../kibana_utils/public/history/index.ts | 20 ++
.../public/history/remove_query_param.test.ts | 75 ++++++
.../public/history/remove_query_param.ts} | 43 ++--
src/plugins/kibana_utils/public/index.ts | 2 +
.../url/kbn_url_storage.test.ts | 54 +++-
.../state_management/url/kbn_url_storage.ts | 41 ++-
.../utils/diff_object.test.ts | 0
.../state_management/utils/diff_object.ts | 0
.../public/state_sync/state_sync.test.ts | 36 +++
.../public/state_sync/state_sync.ts | 14 +-
.../create_kbn_url_state_storage.test.ts | 17 +-
.../create_kbn_url_state_storage.ts | 17 +-
.../apps/dashboard/dashboard_clone.js | 2 +-
30 files changed, 573 insertions(+), 282 deletions(-)
create mode 100644 src/plugins/kibana_utils/public/history/index.ts
create mode 100644 src/plugins/kibana_utils/public/history/remove_query_param.test.ts
rename src/{legacy/core_plugins/kibana/public/dashboard/__tests__/get_app_state_mock.ts => plugins/kibana_utils/public/history/remove_query_param.ts} (53%)
rename src/{legacy/ui => plugins/kibana_utils}/public/state_management/utils/diff_object.test.ts (100%)
rename src/{legacy/ui => plugins/kibana_utils}/public/state_management/utils/diff_object.ts (100%)
diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.ts b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.ts
index 633b7e630700d..ad5c91d2e19de 100644
--- a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.ts
+++ b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.ts
@@ -18,6 +18,7 @@
*/
import _ from 'lodash';
+import { Subscription } from 'rxjs';
import { State } from 'ui/state_management/state';
import { FilterManager, esFilters } from '../../../../../../plugins/data/public';
@@ -28,7 +29,7 @@ import {
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../../plugins/data/public/query/filter_manager/lib/compare_filters';
-type GetAppStateFunc = () => State | undefined | null;
+type GetAppStateFunc = () => { filters?: esFilters.Filter[]; save?: () => void } | undefined | null;
/**
* FilterStateManager is responsible for watching for filter changes
@@ -36,10 +37,12 @@ type GetAppStateFunc = () => State | undefined | null;
* back to the URL.
**/
export class FilterStateManager {
+ private filterManagerUpdatesSubscription: Subscription;
+
filterManager: FilterManager;
globalState: State;
getAppState: GetAppStateFunc;
- interval: NodeJS.Timeout | undefined;
+ interval: number | undefined;
constructor(globalState: State, getAppState: GetAppStateFunc, filterManager: FilterManager) {
this.getAppState = getAppState;
@@ -48,7 +51,7 @@ export class FilterStateManager {
this.watchFilterState();
- this.filterManager.getUpdates$().subscribe(() => {
+ this.filterManagerUpdatesSubscription = this.filterManager.getUpdates$().subscribe(() => {
this.updateAppState();
});
}
@@ -57,12 +60,13 @@ export class FilterStateManager {
if (this.interval) {
clearInterval(this.interval);
}
+ this.filterManagerUpdatesSubscription.unsubscribe();
}
private watchFilterState() {
// This is a temporary solution to remove rootscope.
// Moving forward, state should provide observable subscriptions.
- this.interval = setInterval(() => {
+ this.interval = window.setInterval(() => {
const appState = this.getAppState();
const stateUndefined = !appState || !this.globalState;
if (stateUndefined) return;
@@ -95,7 +99,7 @@ export class FilterStateManager {
private saveState() {
const appState = this.getAppState();
- if (appState) appState.save();
+ if (appState && appState.save) appState.save();
this.globalState.save();
}
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/__tests__/index.ts b/src/legacy/core_plugins/kibana/public/dashboard/__tests__/index.ts
index ab8dfe81163e4..2b992f95695f3 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/__tests__/index.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/__tests__/index.ts
@@ -17,6 +17,5 @@
* under the License.
*/
-export { getAppStateMock } from './get_app_state_mock';
export { getSavedDashboardMock } from './get_saved_dashboard_mock';
export { getEmbeddableFactoryMock } from './get_embeddable_factories_mock';
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts
index b44d1993db23a..244a58e8a65e5 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts
@@ -28,8 +28,6 @@ import chrome from 'ui/chrome';
export const legacyChrome = chrome;
export { State } from 'ui/state_management/state';
-export { AppState } from 'ui/state_management/app_state';
-export { AppStateClass } from 'ui/state_management/app_state';
export { SavedObjectSaveOpts } from 'ui/saved_objects/types';
export { npSetup, npStart } from 'ui/new_platform';
export { IPrivate } from 'ui/private';
@@ -45,8 +43,6 @@ export { GlobalStateProvider } from 'ui/state_management/global_state';
// @ts-ignore
export { StateManagementConfigProvider } from 'ui/state_management/config_provider';
// @ts-ignore
-export { AppStateProvider } from 'ui/state_management/app_state';
-// @ts-ignore
export { PrivateProvider } from 'ui/private/private';
// @ts-ignore
export { EventsProvider } from 'ui/events';
@@ -60,9 +56,7 @@ export { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url/index';
// @ts-ignore
export { confirmModalFactory } from 'ui/modals/confirm_modal';
export { configureAppAngularModule } from 'ui/legacy_compat';
-export { stateMonitorFactory, StateMonitor } from 'ui/state_management/state_monitor_factory';
export { ensureDefaultIndexPattern } from 'ui/legacy_compat';
-export { unhashUrl } from '../../../../../plugins/kibana_utils/public';
export { IInjector } from 'ui/chrome';
export { SavedObjectLoader } from 'ui/saved_objects';
export { VISUALIZE_EMBEDDABLE_TYPE } from '../../../visualizations/public/embeddable';
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts
index 7f7bf7cf47bda..429a7f7279996 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts
@@ -31,7 +31,6 @@ import { Storage } from '../../../../../../plugins/kibana_utils/public';
import {
GlobalStateProvider,
StateManagementConfigProvider,
- AppStateProvider,
PrivateProvider,
EventsProvider,
PersistedState,
@@ -155,12 +154,6 @@ function createLocalStateModule() {
'app/dashboard/Promise',
'app/dashboard/PersistedState',
])
- .factory('AppState', function(Private: any) {
- return Private(AppStateProvider);
- })
- .service('getAppState', function(Private: any) {
- return Private(AppStateProvider).getAppState;
- })
.service('globalState', function(Private: any) {
return Private(GlobalStateProvider);
});
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx
index e9fdc335ba572..f56990ae82e56 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx
@@ -20,12 +20,7 @@
import moment from 'moment';
import { Subscription } from 'rxjs';
-import {
- AppStateClass as TAppStateClass,
- AppState as TAppState,
- IInjector,
- KbnUrl,
-} from '../legacy_imports';
+import { IInjector } from '../legacy_imports';
import { ViewMode } from '../../../../embeddable_api/public/np_ready/public';
import { SavedObjectDashboard } from '../saved_dashboard/saved_dashboard';
@@ -43,7 +38,7 @@ import { RenderDeps } from './application';
export interface DashboardAppScope extends ng.IScope {
dash: SavedObjectDashboard;
- appState: TAppState;
+ appState: DashboardAppState;
screenTitle: string;
model: {
query: Query;
@@ -60,7 +55,6 @@ export interface DashboardAppScope extends ng.IScope {
refreshInterval: any;
panels: SavedDashboardPanel[];
indexPatterns: IIndexPattern[];
- $evalAsync: any;
dashboardViewMode: ViewMode;
expandedPanel?: string;
getShouldShowEditHelp: () => boolean;
@@ -91,8 +85,6 @@ export interface DashboardAppScope extends ng.IScope {
export function initDashboardAppDirective(app: any, deps: RenderDeps) {
app.directive('dashboardApp', function($injector: IInjector) {
- const AppState = $injector.get>('AppState');
- const kbnUrl = $injector.get('kbnUrl');
const confirmModal = $injector.get('confirmModal');
const config = deps.uiSettings;
@@ -105,17 +97,13 @@ export function initDashboardAppDirective(app: any, deps: RenderDeps) {
$routeParams: {
id?: string;
},
- getAppState: any,
globalState: any
) =>
new DashboardAppController({
$route,
$scope,
$routeParams,
- getAppState,
globalState,
- kbnUrl,
- AppStateClass: AppState,
config,
confirmModal,
indexPatterns: deps.npDataStart.indexPatterns,
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx
index 2706b588a2ec4..7da32ac97126f 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx
@@ -17,57 +17,55 @@
* under the License.
*/
-import _ from 'lodash';
+import _, { uniq } from 'lodash';
import { i18n } from '@kbn/i18n';
import React from 'react';
import angular from 'angular';
-import { uniq } from 'lodash';
import { Subscription } from 'rxjs';
+import { createHashHistory } from 'history';
import { DashboardEmptyScreen, DashboardEmptyScreenProps } from './dashboard_empty_screen';
import {
- subscribeWithScope,
ConfirmationButtonTypes,
- showSaveModal,
- SaveResult,
migrateLegacyQuery,
- State,
- AppStateClass as TAppStateClass,
- KbnUrl,
SavedObjectSaveOpts,
- unhashUrl,
+ SaveResult,
+ showSaveModal,
+ State,
+ subscribeWithScope,
} from '../legacy_imports';
import { FilterStateManager } from '../../../../data/public';
import {
+ esFilters,
IndexPattern,
+ IndexPatternsContract,
Query,
SavedQuery,
- IndexPatternsContract,
} from '../../../../../../plugins/data/public';
import {
- DashboardContainer,
DASHBOARD_CONTAINER_TYPE,
+ DashboardContainer,
DashboardContainerFactory,
DashboardContainerInput,
DashboardPanelState,
} from '../../../../dashboard_embeddable_container/public/np_ready/public';
import {
- isErrorEmbeddable,
+ EmbeddableFactoryNotFoundError,
ErrorEmbeddable,
- ViewMode,
+ isErrorEmbeddable,
openAddPanelFlyout,
- EmbeddableFactoryNotFoundError,
+ ViewMode,
} from '../../../../embeddable_api/public/np_ready/public';
-import { DashboardAppState, NavAction, ConfirmModalFn, SavedDashboardPanel } from './types';
+import { ConfirmModalFn, NavAction, SavedDashboardPanel } from './types';
import { showOptionsPopover } from './top_nav/show_options_popover';
import { DashboardSaveModal } from './top_nav/save_modal';
import { showCloneModal } from './top_nav/show_clone_modal';
import { saveDashboard } from './lib';
import { DashboardStateManager } from './dashboard_state_manager';
-import { DashboardConstants, createDashboardEditUrl } from './dashboard_constants';
+import { createDashboardEditUrl, DashboardConstants } from './dashboard_constants';
import { getTopNavConfig } from './top_nav/get_top_nav_config';
import { TopNavIds } from './top_nav/top_nav_ids';
import { getDashboardTitle } from './dashboard_strings';
@@ -78,17 +76,15 @@ import {
SavedObjectFinderProps,
SavedObjectFinderUi,
} from '../../../../../../plugins/kibana_react/public';
+import { removeQueryParam, unhashUrl } from '../../../../../../plugins/kibana_utils/public';
export interface DashboardAppControllerDependencies extends RenderDeps {
$scope: DashboardAppScope;
$route: any;
$routeParams: any;
- getAppState: any;
globalState: State;
indexPatterns: IndexPatternsContract;
dashboardConfig: any;
- kbnUrl: KbnUrl;
- AppStateClass: TAppStateClass;
config: any;
confirmModal: ConfirmModalFn;
}
@@ -103,12 +99,9 @@ export class DashboardAppController {
$scope,
$route,
$routeParams,
- getAppState,
globalState,
dashboardConfig,
localStorage,
- kbnUrl,
- AppStateClass,
indexPatterns,
config,
confirmModal,
@@ -124,7 +117,6 @@ export class DashboardAppController {
},
core: { notifications, overlays, chrome, injectedMetadata, uiSettings, savedObjects, http },
}: DashboardAppControllerDependencies) {
- new FilterStateManager(globalState, getAppState, filterManager);
const queryFilter = filterManager;
let lastReloadRequestTime = 0;
@@ -134,14 +126,30 @@ export class DashboardAppController {
chrome.docTitle.change(dash.title);
}
+ const history = createHashHistory();
const dashboardStateManager = new DashboardStateManager({
savedDashboard: dash,
- AppStateClass,
+ useHashedUrl: config.get('state:storeInSessionStorage'),
hideWriteControls: dashboardConfig.getHideWriteControls(),
kibanaVersion: injectedMetadata.getKibanaVersion(),
+ history,
});
- $scope.appState = dashboardStateManager.getAppState();
+ const filterStateManager = new FilterStateManager(
+ globalState,
+ () => {
+ // Temporary AppState replacement
+ return {
+ set filters(_filters: esFilters.Filter[]) {
+ dashboardStateManager.setFilters(_filters);
+ },
+ get filters() {
+ return dashboardStateManager.appState.filters;
+ },
+ };
+ },
+ filterManager
+ );
// The hash check is so we only update the time filter on dashboard open, not during
// normal cross app navigation.
@@ -316,8 +324,8 @@ export class DashboardAppController {
dirty = true;
}
+ dashboardStateManager.handleDashboardContainerChanges(container);
$scope.$evalAsync(() => {
- dashboardStateManager.handleDashboardContainerChanges(container);
if (dirty) {
updateState();
}
@@ -337,8 +345,8 @@ export class DashboardAppController {
const type = $routeParams[DashboardConstants.ADD_EMBEDDABLE_TYPE];
const id = $routeParams[DashboardConstants.ADD_EMBEDDABLE_ID];
container.addSavedObjectEmbeddable(type, id);
- kbnUrl.removeParam(DashboardConstants.ADD_EMBEDDABLE_TYPE);
- kbnUrl.removeParam(DashboardConstants.ADD_EMBEDDABLE_ID);
+ removeQueryParam(history, DashboardConstants.ADD_EMBEDDABLE_TYPE);
+ removeQueryParam(history, DashboardConstants.ADD_EMBEDDABLE_ID);
}
}
@@ -409,7 +417,9 @@ export class DashboardAppController {
key
];
if (!_.isEqual(containerValue, appStateValue)) {
- (differences as { [key: string]: unknown })[key] = appStateValue;
+ // cloneDeep hack is needed, as there are multiple place, where container's input mutated,
+ // but values from appStateValue are deeply frozen, as they can't be mutated directly
+ (differences as { [key: string]: unknown })[key] = _.cloneDeep(appStateValue);
}
});
@@ -560,11 +570,6 @@ export class DashboardAppController {
);
function updateViewMode(newMode: ViewMode) {
- $scope.topNavMenu = getTopNavConfig(
- newMode,
- navActions,
- dashboardConfig.getHideWriteControls()
- ); // eslint-disable-line no-use-before-define
dashboardStateManager.switchViewMode(newMode);
}
@@ -580,17 +585,28 @@ export class DashboardAppController {
function revertChangesAndExitEditMode() {
dashboardStateManager.resetState();
- kbnUrl.change(
- dash.id ? createDashboardEditUrl(dash.id) : DashboardConstants.CREATE_NEW_DASHBOARD_URL
- );
// This is only necessary for new dashboards, which will default to Edit mode.
updateViewMode(ViewMode.VIEW);
+ // Angular's $location skips this update because of history updates from syncState which happen simultaneously
+ // when calling kbnUrl.change() angular schedules url update and when angular finally starts to process it,
+ // the update is considered outdated and angular skips it
+ // so have to use implementation of dashboardStateManager.changeDashboardUrl, which workarounds those issues
+ dashboardStateManager.changeDashboardUrl(
+ dash.id ? createDashboardEditUrl(dash.id) : DashboardConstants.CREATE_NEW_DASHBOARD_URL
+ );
+
// We need to do a hard reset of the timepicker. appState will not reload like
// it does on 'open' because it's been saved to the url and the getAppState.previouslyStored() check on
// reload will cause it not to sync.
if (dashboardStateManager.getIsTimeSavedWithDashboard()) {
- dashboardStateManager.syncTimefilterWithDashboard(timefilter);
+ // have to use $evalAsync here until '_g' is migrated from $location to state sync utility ('history')
+ // When state sync utility changes url, angular's $location is missing it's own updates which happen during the same digest cycle
+ // temporary solution is to delay $location updates to next digest cycle
+ // unfortunately, these causes 2 browser history entries, but this is temporary and will be fixed after migrating '_g' to state_sync utilities
+ $scope.$evalAsync(() => {
+ dashboardStateManager.syncTimefilterWithDashboard(timefilter);
+ });
}
}
@@ -642,7 +658,11 @@ export class DashboardAppController {
});
if (dash.id !== $routeParams.id) {
- kbnUrl.change(createDashboardEditUrl(dash.id));
+ // Angular's $location skips this update because of history updates from syncState which happen simultaneously
+ // when calling kbnUrl.change() angular schedules url update and when angular finally starts to process it,
+ // the update is considered outdated and angular skips it
+ // so have to use implementation of dashboardStateManager.changeDashboardUrl, which workarounds those issues
+ dashboardStateManager.changeDashboardUrl(createDashboardEditUrl(dash.id));
} else {
chrome.docTitle.change(dash.lastSavedTitle);
updateViewMode(ViewMode.VIEW);
@@ -844,6 +864,15 @@ export class DashboardAppController {
});
});
+ dashboardStateManager.registerChangeListener(() => {
+ // view mode could have changed, so trigger top nav update
+ $scope.topNavMenu = getTopNavConfig(
+ dashboardStateManager.getViewMode(),
+ navActions,
+ dashboardConfig.getHideWriteControls()
+ );
+ });
+
$scope.$on('$destroy', () => {
updateSubscription.unsubscribe();
visibleSubscription.unsubscribe();
@@ -859,6 +888,9 @@ export class DashboardAppController {
if (dashboardContainer) {
dashboardContainer.destroy();
}
+ if (filterStateManager) {
+ filterStateManager.destroy();
+ }
});
}
}
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state.test.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state.test.ts
index d9a93b2ceedc3..8806684aab13c 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state.test.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state.test.ts
@@ -18,16 +18,12 @@
*/
import './np_core.test.mocks';
+import { createBrowserHistory } from 'history';
import { DashboardStateManager } from './dashboard_state_manager';
-import { getAppStateMock, getSavedDashboardMock } from '../__tests__';
-import { AppStateClass } from '../legacy_imports';
-import { DashboardAppState } from './types';
-import { TimeRange, TimefilterContract, InputTimeRange } from 'src/plugins/data/public';
+import { getSavedDashboardMock } from '../__tests__';
+import { InputTimeRange, TimefilterContract, TimeRange } from 'src/plugins/data/public';
import { ViewMode } from 'src/plugins/embeddable/public';
-jest.mock('ui/state_management/state', () => ({
- State: {},
-}));
jest.mock('ui/agg_types', () => ({
aggTypes: {
metrics: [],
@@ -52,9 +48,10 @@ describe('DashboardState', function() {
function initDashboardState() {
dashboardState = new DashboardStateManager({
savedDashboard,
- AppStateClass: getAppStateMock() as AppStateClass,
+ useHashedUrl: false,
hideWriteControls: false,
kibanaVersion: '7.0.0',
+ history: createBrowserHistory(),
});
}
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state_manager.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state_manager.ts
index 6df18757da6f5..451e7c8ff96db 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state_manager.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state_manager.ts
@@ -19,20 +19,16 @@
import { i18n } from '@kbn/i18n';
import _ from 'lodash';
-
+import { History } from 'history';
+import { Subscription } from 'rxjs';
import { Moment } from 'moment';
import { DashboardContainer } from 'src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public';
import { ViewMode } from '../../../../../../plugins/embeddable/public';
+import { migrateLegacyQuery } from '../legacy_imports';
import {
- stateMonitorFactory,
- StateMonitor,
- AppStateClass as TAppStateClass,
- migrateLegacyQuery,
-} from '../legacy_imports';
-import {
- Query,
esFilters,
+ Query,
TimefilterContract as Timefilter,
} from '../../../../../../plugins/data/public';
@@ -41,7 +37,20 @@ import { convertPanelStateToSavedDashboardPanel } from './lib/embeddable_saved_o
import { FilterUtils } from './lib/filter_utils';
import { SavedObjectDashboard } from '../saved_dashboard/saved_dashboard';
-import { SavedDashboardPanel, DashboardAppState, DashboardAppStateDefaults } from './types';
+import {
+ DashboardAppState,
+ DashboardAppStateDefaults,
+ DashboardAppStateTransitions,
+ SavedDashboardPanel,
+} from './types';
+import {
+ createKbnUrlStateStorage,
+ createStateContainer,
+ IKbnUrlStateStorage,
+ ISyncStateRef,
+ ReduxLikeStateContainer,
+ syncState,
+} from '../../../../../../plugins/kibana_utils/public';
/**
* Dashboard state manager handles connecting angular and redux state between the angular and react portions of the
@@ -51,7 +60,6 @@ import { SavedDashboardPanel, DashboardAppState, DashboardAppStateDefaults } fro
*/
export class DashboardStateManager {
public savedDashboard: SavedObjectDashboard;
- public appState: DashboardAppState;
public lastSavedDashboardFilters: {
timeTo?: string | Moment;
timeFrom?: string | Moment;
@@ -63,38 +71,78 @@ export class DashboardStateManager {
private kibanaVersion: string;
public isDirty: boolean;
private changeListeners: Array<(status: { dirty: boolean }) => void>;
- private stateMonitor: StateMonitor;
+
+ public get appState(): DashboardAppState {
+ return this.stateContainer.get();
+ }
+
+ private readonly stateContainer: ReduxLikeStateContainer<
+ DashboardAppState,
+ DashboardAppStateTransitions
+ >;
+ private readonly stateContainerChangeSub: Subscription;
+ private readonly STATE_STORAGE_KEY = '_a';
+ private readonly kbnUrlStateStorage: IKbnUrlStateStorage;
+ private readonly stateSyncRef: ISyncStateRef;
+ private readonly history: History;
/**
*
* @param savedDashboard
- * @param AppState The AppState class to use when instantiating a new AppState instance.
* @param hideWriteControls true if write controls should be hidden.
+ * @param kibanaVersion current kibanaVersion
+ * @param
*/
constructor({
savedDashboard,
- AppStateClass,
hideWriteControls,
kibanaVersion,
+ useHashedUrl,
+ history,
}: {
savedDashboard: SavedObjectDashboard;
- AppStateClass: TAppStateClass;
hideWriteControls: boolean;
kibanaVersion: string;
+ useHashedUrl: boolean;
+ history: History;
}) {
+ this.history = history;
this.kibanaVersion = kibanaVersion;
this.savedDashboard = savedDashboard;
this.hideWriteControls = hideWriteControls;
- this.stateDefaults = getAppStateDefaults(this.savedDashboard, this.hideWriteControls);
+ // get state defaults from saved dashboard, make sure it is migrated
+ this.stateDefaults = migrateAppState(
+ getAppStateDefaults(this.savedDashboard, this.hideWriteControls),
+ kibanaVersion
+ );
+
+ this.kbnUrlStateStorage = createKbnUrlStateStorage({ useHash: useHashedUrl, history });
- this.appState = new AppStateClass(this.stateDefaults);
+ // setup initial state by merging defaults with state from url
+ // also run migration, as state in url could be of older version
+ const initialState = migrateAppState(
+ {
+ ...this.stateDefaults,
+ ...this.kbnUrlStateStorage.get(this.STATE_STORAGE_KEY),
+ },
+ kibanaVersion
+ );
- // Initializing appState does two things - first it translates the defaults into AppState, second it updates
- // appState based on the URL (the url trumps the defaults). This means if we update the state format at all and
- // want to handle BWC, we must not only migrate the data stored with saved Dashboard, but also any old state in the
- // url.
- migrateAppState(this.appState, kibanaVersion);
+ // setup state container using initial state both from defaults and from url
+ this.stateContainer = createStateContainer(
+ initialState,
+ {
+ set: state => (prop, value) => ({ ...state, [prop]: value }),
+ setOption: state => (option, value) => ({
+ ...state,
+ options: {
+ ...state.options,
+ [option]: value,
+ },
+ }),
+ }
+ );
this.isDirty = false;
@@ -104,29 +152,35 @@ export class DashboardStateManager {
// in the 'lose changes' warning message.
this.lastSavedDashboardFilters = this.getFilterState();
- /**
- * Creates a state monitor and saves it to this.stateMonitor. Used to track unsaved changes made to appState.
- */
- this.stateMonitor = stateMonitorFactory.create(
- this.appState,
- this.stateDefaults
- );
-
- this.stateMonitor.ignoreProps('viewMode');
- // Filters need to be compared manually because they sometimes have a $$hashkey stored on the object.
- this.stateMonitor.ignoreProps('filters');
- // Query needs to be compared manually because saved legacy queries get migrated in app state automatically
- this.stateMonitor.ignoreProps('query');
+ this.changeListeners = [];
- this.stateMonitor.onChange((status: { dirty: boolean }) => {
- this.isDirty = status.dirty;
+ this.stateContainerChangeSub = this.stateContainer.state$.subscribe(() => {
+ this.isDirty = this.checkIsDirty();
+ this.changeListeners.forEach(listener => listener({ dirty: this.isDirty }));
});
- this.changeListeners = [];
-
- this.stateMonitor.onChange((status: { dirty: boolean }) => {
- this.changeListeners.forEach(listener => listener(status));
+ // make sure url ('_a') matches initial state
+ this.kbnUrlStateStorage.set(this.STATE_STORAGE_KEY, initialState, { replace: true });
+
+ // setup state syncing utils. state container will be synched with url into `this.STATE_STORAGE_KEY` query param
+ this.stateSyncRef = syncState({
+ storageKey: this.STATE_STORAGE_KEY,
+ stateContainer: {
+ ...this.stateContainer,
+ set: (state: DashboardAppState | null) => {
+ // sync state required state container to be able to handle null
+ // overriding set() so it could handle null coming from url
+ this.stateContainer.set({
+ ...this.stateDefaults,
+ ...state,
+ });
+ },
+ },
+ stateStorage: this.kbnUrlStateStorage,
});
+
+ // actually start syncing state with container
+ this.stateSyncRef.start();
}
public registerChangeListener(callback: (status: { dirty: boolean }) => void) {
@@ -172,7 +226,7 @@ export class DashboardStateManager {
});
if (dirty) {
- this.appState.panels = Object.values(convertedPanelStateMap);
+ this.stateContainer.transitions.set('panels', Object.values(convertedPanelStateMap));
}
if (input.isFullScreenMode !== this.getFullScreenMode()) {
@@ -184,7 +238,6 @@ export class DashboardStateManager {
}
this.changeListeners.forEach(listener => listener({ dirty }));
- this.saveState();
}
public getFullScreenMode() {
@@ -192,8 +245,11 @@ export class DashboardStateManager {
}
public setFullScreenMode(fullScreenMode: boolean) {
- this.appState.fullScreenMode = fullScreenMode;
- this.saveState();
+ this.stateContainer.transitions.set('fullScreenMode', fullScreenMode);
+ }
+
+ public setFilters(filters: esFilters.Filter[]) {
+ this.stateContainer.transitions.set('filters', filters);
}
/**
@@ -210,7 +266,10 @@ export class DashboardStateManager {
// The right way to fix this might be to ensure the defaults object stored on state is a deep
// clone, but given how much code uses the state object, I determined that to be too risky of a change for
// now. TODO: revisit this!
- this.stateDefaults = getAppStateDefaults(this.savedDashboard, this.hideWriteControls);
+ this.stateDefaults = migrateAppState(
+ getAppStateDefaults(this.savedDashboard, this.hideWriteControls),
+ this.kibanaVersion
+ );
// The original query won't be restored by the above because the query on this.savedDashboard is applied
// in place in order for it to affect the visualizations.
this.stateDefaults.query = this.lastSavedDashboardFilters.query;
@@ -218,9 +277,7 @@ export class DashboardStateManager {
this.stateDefaults.filters = [...this.getLastSavedFilterBars()];
this.isDirty = false;
- this.appState.setDefaults(this.stateDefaults);
- this.appState.reset();
- this.stateMonitor.setInitialState(this.appState.toJSON());
+ this.stateContainer.set(this.stateDefaults);
}
/**
@@ -252,31 +309,28 @@ export class DashboardStateManager {
}
public setDescription(description: string) {
- this.appState.description = description;
- this.saveState();
+ this.stateContainer.transitions.set('description', description);
}
public setTitle(title: string) {
- this.appState.title = title;
this.savedDashboard.title = title;
- this.saveState();
+ this.stateContainer.transitions.set('title', title);
}
public getAppState() {
- return this.appState;
+ return this.stateContainer.get();
}
public getQuery(): Query {
- return migrateLegacyQuery(this.appState.query);
+ return migrateLegacyQuery(this.stateContainer.get().query);
}
public getSavedQueryId() {
- return this.appState.savedQuery;
+ return this.stateContainer.get().savedQuery;
}
public setSavedQueryId(id?: string) {
- this.appState.savedQuery = id;
- this.saveState();
+ this.stateContainer.transitions.set('savedQuery', id);
}
public getUseMargins() {
@@ -287,8 +341,7 @@ export class DashboardStateManager {
}
public setUseMargins(useMargins: boolean) {
- this.appState.options.useMargins = useMargins;
- this.saveState();
+ this.stateContainer.transitions.setOption('useMargins', useMargins);
}
public getHidePanelTitles() {
@@ -296,8 +349,7 @@ export class DashboardStateManager {
}
public setHidePanelTitles(hidePanelTitles: boolean) {
- this.appState.options.hidePanelTitles = hidePanelTitles;
- this.saveState();
+ this.stateContainer.transitions.setOption('hidePanelTitles', hidePanelTitles);
}
public getTimeRestore() {
@@ -305,8 +357,7 @@ export class DashboardStateManager {
}
public setTimeRestore(timeRestore: boolean) {
- this.appState.timeRestore = timeRestore;
- this.saveState();
+ this.stateContainer.transitions.set('timeRestore', timeRestore);
}
public getIsTimeSavedWithDashboard() {
@@ -397,7 +448,6 @@ export class DashboardStateManager {
(panel: SavedDashboardPanel) => panel.panelIndex === panelIndex
);
Object.assign(foundPanel, panelAttributes);
- this.saveState();
return foundPanel;
}
@@ -456,15 +506,37 @@ export class DashboardStateManager {
}
/**
- * Saves the current application state to the URL.
+ * Synchronously writes current state to url
+ * returned boolean indicates whether the update happened and if history was updated
*/
- public saveState() {
- this.appState.save();
+ private saveState({ replace }: { replace: boolean }): boolean {
+ // schedules setting current state to url
+ this.kbnUrlStateStorage.set(
+ this.STATE_STORAGE_KEY,
+ this.stateContainer.get()
+ );
+ // immediately forces scheduled updates and changes location
+ return this.kbnUrlStateStorage.flush({ replace });
+ }
+
+ // TODO: find nicer solution for this
+ // this function helps to make just 1 browser history update, when we imperatively changing the dashboard url
+ // It could be that there is pending *dashboardStateManager* updates, which aren't flushed yet to the url.
+ // So to prevent 2 browser updates:
+ // 1. Force flush any pending state updates (syncing state to query)
+ // 2. If url was updated, then apply path change with replace
+ public changeDashboardUrl(pathname: string) {
+ // synchronously persist current state to url with push()
+ const updated = this.saveState({ replace: false });
+ // change pathname
+ this.history[updated ? 'replace' : 'push']({
+ ...this.history.location,
+ pathname,
+ });
}
public setQuery(query: Query) {
- this.appState.query = query;
- this.saveState();
+ this.stateContainer.transitions.set('query', query);
}
/**
@@ -472,24 +544,33 @@ export class DashboardStateManager {
* @param filter An array of filter bar filters.
*/
public applyFilters(query: Query, filters: esFilters.Filter[]) {
- this.appState.query = query;
this.savedDashboard.searchSource.setField('query', query);
this.savedDashboard.searchSource.setField('filter', filters);
- this.saveState();
+ this.stateContainer.transitions.set('query', query);
}
public switchViewMode(newMode: ViewMode) {
- this.appState.viewMode = newMode;
- this.saveState();
+ this.stateContainer.transitions.set('viewMode', newMode);
}
/**
* Destroys and cleans up this object when it's no longer used.
*/
public destroy() {
- if (this.stateMonitor) {
- this.stateMonitor.destroy();
- }
+ this.stateContainerChangeSub.unsubscribe();
this.savedDashboard.destroy();
+ if (this.stateSyncRef) {
+ this.stateSyncRef.stop();
+ }
+ }
+
+ private checkIsDirty() {
+ // Filters need to be compared manually because they sometimes have a $$hashkey stored on the object.
+ // Query needs to be compared manually because saved legacy queries get migrated in app state automatically
+ const propsToIgnore: Array = ['viewMode', 'filters', 'query'];
+
+ const initial = _.omit(this.stateDefaults, propsToIgnore);
+ const current = _.omit(this.stateContainer.get(), propsToIgnore);
+ return !_.isEqual(initial, current);
}
}
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js
index 540bfcf5aa684..7dc408ea4b801 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js
@@ -35,6 +35,7 @@ import {
import { DashboardListing, EMPTY_FILTER } from './listing/dashboard_listing';
import { addHelpMenuToAppChrome } from './help_menu/help_menu_util';
import { syncOnMount } from './global_state_sync';
+import { createHashHistory } from 'history';
export function initDashboardApp(app, deps) {
initDashboardAppDirective(app, deps);
@@ -190,7 +191,7 @@ export function initDashboardApp(app, deps) {
template: dashboardTemplate,
controller: createNewDashboardCtrl,
resolve: {
- dash: function($rootScope, $route, redirectWhenMissing, kbnUrl, AppState) {
+ dash: function($rootScope, $route, redirectWhenMissing, kbnUrl) {
const id = $route.current.params.id;
return ensureDefaultIndexPattern(deps.core, deps.npDataStart, $rootScope, kbnUrl)
@@ -216,8 +217,13 @@ export function initDashboardApp(app, deps) {
// Preserve BWC of v5.3.0 links for new, unsaved dashboards.
// See https://github.com/elastic/kibana/issues/10951 for more context.
if (error instanceof SavedObjectNotFound && id === 'create') {
- // Note "new AppState" is necessary so the state in the url is preserved through the redirect.
- kbnUrl.redirect(DashboardConstants.CREATE_NEW_DASHBOARD_URL, {}, new AppState());
+ // Note preserve querystring part is necessary so the state is preserved through the redirect.
+ const history = createHashHistory();
+ history.replace({
+ ...history.location, // preserve query,
+ pathname: DashboardConstants.CREATE_NEW_DASHBOARD_URL,
+ });
+
deps.toastNotifications.addWarning(
i18n.translate('kbn.dashboard.urlWasRemovedInSixZeroWarningMessage', {
defaultMessage:
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/migrate_app_state.test.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/migrate_app_state.test.ts
index 4aa2461bb6593..73336ec951894 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/migrate_app_state.test.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/migrate_app_state.test.ts
@@ -23,7 +23,6 @@ import { SavedDashboardPanel } from '../types';
import { migrateAppState } from './migrate_app_state';
test('migrate app state from 6.0', async () => {
- const mockSave = jest.fn();
const appState = {
uiState: {
'P-1': { vis: { defaultColors: { '0+-+100': 'rgb(0,104,55)' } } },
@@ -39,11 +38,8 @@ test('migrate app state from 6.0', async () => {
type: 'visualization',
},
],
- translateHashToRison: () => 'a',
- getQueryParamName: () => 'a',
- save: mockSave,
};
- migrateAppState(appState, '8.0');
+ migrateAppState(appState as any, '8.0');
expect(appState.uiState).toBeUndefined();
const newPanel = (appState.panels[0] as unknown) as SavedDashboardPanel;
@@ -54,12 +50,10 @@ test('migrate app state from 6.0', async () => {
expect(newPanel.gridData.y).toBe(0);
expect((newPanel.embeddableConfig as any).vis.defaultColors['0+-+100']).toBe('rgb(0,104,55)');
- expect(mockSave).toBeCalledTimes(1);
});
test('migrate sort from 6.1', async () => {
const TARGET_VERSION = '8.0';
- const mockSave = jest.fn();
const appState = {
uiState: {
'P-1': { vis: { defaultColors: { '0+-+100': 'rgb(0,104,55)' } } },
@@ -76,12 +70,9 @@ test('migrate sort from 6.1', async () => {
sort: 'sort',
},
],
- translateHashToRison: () => 'a',
- getQueryParamName: () => 'a',
- save: mockSave,
useMargins: false,
};
- migrateAppState(appState, TARGET_VERSION);
+ migrateAppState(appState as any, TARGET_VERSION);
expect(appState.uiState).toBeUndefined();
const newPanel = (appState.panels[0] as unknown) as SavedDashboardPanel;
@@ -91,11 +82,9 @@ test('migrate sort from 6.1', async () => {
expect((newPanel.embeddableConfig as any).sort).toBe('sort');
expect((newPanel.embeddableConfig as any).vis.defaultColors['0+-+100']).toBe('rgb(0,104,55)');
- expect(mockSave).toBeCalledTimes(1);
});
test('migrates 6.0 even when uiState does not exist', async () => {
- const mockSave = jest.fn();
const appState = {
panels: [
{
@@ -109,11 +98,8 @@ test('migrates 6.0 even when uiState does not exist', async () => {
sort: 'sort',
},
],
- translateHashToRison: () => 'a',
- getQueryParamName: () => 'a',
- save: mockSave,
};
- migrateAppState(appState, '8.0');
+ migrateAppState(appState as any, '8.0');
expect((appState as any).uiState).toBeUndefined();
const newPanel = (appState.panels[0] as unknown) as SavedDashboardPanel;
@@ -122,11 +108,9 @@ test('migrates 6.0 even when uiState does not exist', async () => {
expect((newPanel as any).sort).toBeUndefined();
expect((newPanel.embeddableConfig as any).sort).toBe('sort');
- expect(mockSave).toBeCalledTimes(1);
});
test('6.2 migration adjusts w & h without margins', async () => {
- const mockSave = jest.fn();
const appState = {
panels: [
{
@@ -143,12 +127,9 @@ test('6.2 migration adjusts w & h without margins', async () => {
version: '6.2.0',
},
],
- translateHashToRison: () => 'a',
- getQueryParamName: () => 'a',
- save: mockSave,
useMargins: false,
};
- migrateAppState(appState, '8.0');
+ migrateAppState(appState as any, '8.0');
expect((appState as any).uiState).toBeUndefined();
const newPanel = (appState.panels[0] as unknown) as SavedDashboardPanel;
@@ -159,11 +140,9 @@ test('6.2 migration adjusts w & h without margins', async () => {
expect((newPanel as any).sort).toBeUndefined();
expect((newPanel.embeddableConfig as any).sort).toBe('sort');
- expect(mockSave).toBeCalledTimes(1);
});
test('6.2 migration adjusts w & h with margins', async () => {
- const mockSave = jest.fn();
const appState = {
panels: [
{
@@ -180,12 +159,9 @@ test('6.2 migration adjusts w & h with margins', async () => {
version: '6.2.0',
},
],
- translateHashToRison: () => 'a',
- getQueryParamName: () => 'a',
- save: mockSave,
useMargins: true,
};
- migrateAppState(appState, '8.0');
+ migrateAppState(appState as any, '8.0');
expect((appState as any).uiState).toBeUndefined();
const newPanel = (appState.panels[0] as unknown) as SavedDashboardPanel;
@@ -196,5 +172,4 @@ test('6.2 migration adjusts w & h with margins', async () => {
expect((newPanel as any).sort).toBeUndefined();
expect((newPanel.embeddableConfig as any).sort).toBe('sort');
- expect(mockSave).toBeCalledTimes(1);
});
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/migrate_app_state.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/migrate_app_state.ts
index 4083900c7ede7..0cd958ced0eb1 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/migrate_app_state.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/migrate_app_state.ts
@@ -28,6 +28,7 @@ import {
SavedDashboardPanel630,
SavedDashboardPanel640To720,
SavedDashboardPanel620,
+ SavedDashboardPanel,
} from '../types';
import { migratePanelsTo730 } from '../../migrations/migrate_to_730_panels';
@@ -37,9 +38,9 @@ import { migratePanelsTo730 } from '../../migrations/migrate_to_730_panels';
* Once we hit a major version, we can remove support for older style URLs and get rid of this logic.
*/
export function migrateAppState(
- appState: { [key: string]: unknown } | DashboardAppState,
+ appState: { [key: string]: unknown } & DashboardAppState,
kibanaVersion: string
-) {
+): DashboardAppState {
if (!appState.panels) {
throw new Error(
i18n.translate('kbn.dashboard.panel.invalidData', {
@@ -76,11 +77,11 @@ export function migrateAppState(
| SavedDashboardPanel640To720
>,
kibanaVersion,
- appState.useMargins,
- appState.uiState
- );
+ appState.useMargins as boolean,
+ appState.uiState as Record>
+ ) as SavedDashboardPanel[];
delete appState.uiState;
-
- appState.save();
}
+
+ return appState;
}
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/save_dashboard.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/save_dashboard.ts
index 691c87122564f..d80208ce27ffe 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/save_dashboard.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/save_dashboard.ts
@@ -36,16 +36,19 @@ export function saveDashboard(
dashboardStateManager: DashboardStateManager,
saveOptions: SavedObjectSaveOpts
): Promise {
- dashboardStateManager.saveState();
-
const savedDashboard = dashboardStateManager.savedDashboard;
const appState = dashboardStateManager.appState;
updateSavedDashboard(savedDashboard, appState, timeFilter, toJson);
return savedDashboard.save(saveOptions).then((id: string) => {
- dashboardStateManager.lastSavedDashboardFilters = dashboardStateManager.getFilterState();
- dashboardStateManager.resetState();
+ if (id) {
+ // reset state only when save() was successful
+ // e.g. save() could be interrupted if title is duplicated and not confirmed
+ dashboardStateManager.lastSavedDashboardFilters = dashboardStateManager.getFilterState();
+ dashboardStateManager.resetState();
+ }
+
return id;
});
}
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/update_saved_dashboard.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/update_saved_dashboard.ts
index 2072b5d4f6eb0..ec8073c0f72f7 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/update_saved_dashboard.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/update_saved_dashboard.ts
@@ -19,13 +19,13 @@
import _ from 'lodash';
import { RefreshInterval, TimefilterContract } from 'src/plugins/data/public';
-import { AppState } from '../../legacy_imports';
import { FilterUtils } from './filter_utils';
import { SavedObjectDashboard } from '../../saved_dashboard/saved_dashboard';
+import { DashboardAppState } from '../types';
export function updateSavedDashboard(
savedDashboard: SavedObjectDashboard,
- appState: AppState,
+ appState: DashboardAppState,
timeFilter: TimefilterContract,
toJson: (object: T) => string
) {
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/types.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/types.ts
index e3eb25a208856..3151fbf821b9f 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/types.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/types.ts
@@ -18,7 +18,6 @@
*/
import { ViewMode } from 'src/plugins/embeddable/public';
-import { AppState } from '../legacy_imports';
import {
RawSavedDashboardPanelTo60,
RawSavedDashboardPanel610,
@@ -93,11 +92,7 @@ export type SavedDashboardPanelTo60 = Pick<
readonly type: string;
};
-export type DashboardAppStateDefaults = DashboardAppStateParameters & {
- description?: string;
-};
-
-export interface DashboardAppStateParameters {
+export interface DashboardAppState {
panels: SavedDashboardPanel[];
fullScreenMode: boolean;
title: string;
@@ -113,9 +108,24 @@ export interface DashboardAppStateParameters {
savedQuery?: string;
}
-// This could probably be improved if we flesh out AppState more... though AppState will be going away
-// so maybe not worth too much time atm.
-export type DashboardAppState = DashboardAppStateParameters & AppState;
+export type DashboardAppStateDefaults = DashboardAppState & {
+ description?: string;
+};
+
+export interface DashboardAppStateTransitions {
+ set: (
+ state: DashboardAppState
+ ) => (
+ prop: T,
+ value: DashboardAppState[T]
+ ) => DashboardAppState;
+ setOption: (
+ state: DashboardAppState
+ ) => (
+ prop: T,
+ value: DashboardAppState['options'][T]
+ ) => DashboardAppState;
+}
export interface SavedDashboardPanelMap {
[key: string]: SavedDashboardPanel;
@@ -139,18 +149,3 @@ export type ConfirmModalFn = (
title: string;
}
) => void;
-
-export type AddFilterFn = (
- {
- field,
- value,
- operator,
- index,
- }: {
- field: string;
- value: string;
- operator: string;
- index: string;
- },
- appState: AppState
-) => void;
diff --git a/src/legacy/ui/public/chrome/api/controls.ts b/src/legacy/ui/public/chrome/api/controls.ts
index 6dde26be20df2..a95d53ec2eab6 100644
--- a/src/legacy/ui/public/chrome/api/controls.ts
+++ b/src/legacy/ui/public/chrome/api/controls.ts
@@ -42,4 +42,6 @@ export function initChromeControlsApi(chrome: { [key: string]: any }) {
* might be incorrect in the moments just before the UI is updated.
*/
chrome.getVisible = () => visible$.getValue();
+
+ chrome.visible$ = visible$.asObservable();
}
diff --git a/src/legacy/ui/public/chrome/directives/kbn_chrome.js b/src/legacy/ui/public/chrome/directives/kbn_chrome.js
index 72d26a37a60a1..4c5d7981e962a 100644
--- a/src/legacy/ui/public/chrome/directives/kbn_chrome.js
+++ b/src/legacy/ui/public/chrome/directives/kbn_chrome.js
@@ -30,6 +30,7 @@ import {
chromeHeaderNavControlsRegistry,
NavControlSide,
} from '../../registry/chrome_header_nav_controls';
+import { subscribeWithScope } from '../../utils/subscribe_with_scope';
export function kbnChromeProvider(chrome, internals) {
uiModules.get('kibana').directive('kbnChrome', () => {
@@ -83,6 +84,15 @@ export function kbnChromeProvider(chrome, internals) {
);
}
+ const chromeVisibility = subscribeWithScope($scope, chrome.visible$, {
+ next: () => {
+ // just makes sure change detection is triggered when chrome visibility changes
+ },
+ });
+ $scope.$on('$destroy', () => {
+ chromeVisibility.unsubscribe();
+ });
+
return chrome;
},
};
diff --git a/src/legacy/ui/public/state_management/state.js b/src/legacy/ui/public/state_management/state.js
index 289d4b8006cba..c2274eae59f50 100644
--- a/src/legacy/ui/public/state_management/state.js
+++ b/src/legacy/ui/public/state_management/state.js
@@ -29,7 +29,6 @@ import _ from 'lodash';
import { i18n } from '@kbn/i18n';
import angular from 'angular';
import rison from 'rison-node';
-import { applyDiff } from './utils/diff_object';
import { EventsProvider } from '../events';
import { fatalError, toastNotifications } from '../notify';
import './config_provider';
@@ -38,6 +37,7 @@ import {
hashedItemStore,
isStateHash,
createStateHash,
+ applyDiff,
} from '../../../../plugins/kibana_utils/public';
export function StateProvider(
diff --git a/src/plugins/kibana_utils/public/history/index.ts b/src/plugins/kibana_utils/public/history/index.ts
new file mode 100644
index 0000000000000..b4b5658c1c886
--- /dev/null
+++ b/src/plugins/kibana_utils/public/history/index.ts
@@ -0,0 +1,20 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export { removeQueryParam } from './remove_query_param';
diff --git a/src/plugins/kibana_utils/public/history/remove_query_param.test.ts b/src/plugins/kibana_utils/public/history/remove_query_param.test.ts
new file mode 100644
index 0000000000000..0b2547ae94668
--- /dev/null
+++ b/src/plugins/kibana_utils/public/history/remove_query_param.test.ts
@@ -0,0 +1,75 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { removeQueryParam } from './remove_query_param';
+import { createMemoryHistory, Location } from 'history';
+
+describe('removeQueryParam', () => {
+ it('should remove query param from url', () => {
+ const startLocation: Location = {
+ pathname: '/dashboard/c3a76790-3134-11ea-b024-83a7b4783735',
+ search: "?_a=(description:'')&_b=3",
+ state: null,
+ hash: '',
+ };
+
+ const history = createMemoryHistory();
+ history.push(startLocation);
+ removeQueryParam(history, '_a');
+
+ expect(history.location).toEqual(
+ expect.objectContaining({
+ pathname: '/dashboard/c3a76790-3134-11ea-b024-83a7b4783735',
+ search: '?_b=3',
+ state: null,
+ hash: '',
+ })
+ );
+ });
+
+ it('should not fail if nothing to remove', () => {
+ const startLocation: Location = {
+ pathname: '/dashboard/c3a76790-3134-11ea-b024-83a7b4783735',
+ search: "?_a=(description:'')&_b=3",
+ state: null,
+ hash: '',
+ };
+
+ const history = createMemoryHistory();
+ history.push(startLocation);
+ removeQueryParam(history, '_c');
+
+ expect(history.location).toEqual(expect.objectContaining(startLocation));
+ });
+
+ it('should not fail if no search', () => {
+ const startLocation: Location = {
+ pathname: '/dashboard/c3a76790-3134-11ea-b024-83a7b4783735',
+ search: '',
+ state: null,
+ hash: '',
+ };
+
+ const history = createMemoryHistory();
+ history.push(startLocation);
+ removeQueryParam(history, '_c');
+
+ expect(history.location).toEqual(expect.objectContaining(startLocation));
+ });
+});
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/__tests__/get_app_state_mock.ts b/src/plugins/kibana_utils/public/history/remove_query_param.ts
similarity index 53%
rename from src/legacy/core_plugins/kibana/public/dashboard/__tests__/get_app_state_mock.ts
rename to src/plugins/kibana_utils/public/history/remove_query_param.ts
index d9dea35a8a1c0..fbf985998b4cd 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/__tests__/get_app_state_mock.ts
+++ b/src/plugins/kibana_utils/public/history/remove_query_param.ts
@@ -17,32 +17,23 @@
* under the License.
*/
-import { AppStateClass } from '../legacy_imports';
+import { History, Location } from 'history';
+import { parse } from 'querystring';
+import { stringifyQueryString } from '../state_management/url/stringify_query_string'; // TODO: extract it to ../url
-/**
- * A poor excuse for a mock just to get some basic tests to run in jest without requiring the injector.
- * This could be improved if we extract the appState and state classes externally of their angular providers.
- * @return {AppStateMock}
- */
-export function getAppStateMock(): AppStateClass {
- class AppStateMock {
- constructor(defaults: any) {
- Object.assign(this, defaults);
- }
-
- on() {}
- off() {}
- toJSON() {
- return '';
- }
- save() {}
- translateHashToRison(stateHashOrRison: string | string[]) {
- return stateHashOrRison;
- }
- getQueryParamName() {
- return '';
- }
+export function removeQueryParam(history: History, param: string, replace: boolean = true) {
+ const oldLocation = history.location;
+ const search = (oldLocation.search || '').replace(/^\?/, '');
+ const query = parse(search);
+ delete query[param];
+ const newSearch = stringifyQueryString(query);
+ const newLocation: Location = {
+ ...oldLocation,
+ search: newSearch,
+ };
+ if (replace) {
+ history.replace(newLocation);
+ } else {
+ history.push(newLocation);
}
-
- return AppStateMock;
}
diff --git a/src/plugins/kibana_utils/public/index.ts b/src/plugins/kibana_utils/public/index.ts
index fa58a61e51232..00c1c95028b4d 100644
--- a/src/plugins/kibana_utils/public/index.ts
+++ b/src/plugins/kibana_utils/public/index.ts
@@ -58,3 +58,5 @@ export {
StartSyncStateFnType,
StopSyncStateFnType,
} from './state_sync';
+export { removeQueryParam } from './history';
+export { applyDiff } from './state_management/utils/diff_object';
diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts
index f1c527d3d5309..6e4c505c62ebc 100644
--- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts
+++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts
@@ -85,6 +85,7 @@ describe('kbn_url_storage', () => {
beforeEach(() => {
history = createMemoryHistory();
urlControls = createKbnUrlControls(history);
+ urlControls.update('/', true);
});
const getCurrentUrl = () => createPath(history.location);
@@ -143,17 +144,6 @@ describe('kbn_url_storage', () => {
expect(cb).toHaveBeenCalledTimes(3);
});
- it('should flush async url updates', async () => {
- const pr1 = urlControls.updateAsync(() => '/1', false);
- const pr2 = urlControls.updateAsync(() => '/2', false);
- const pr3 = urlControls.updateAsync(() => '/3', false);
- expect(getCurrentUrl()).toBe('/');
- urlControls.flush();
- expect(getCurrentUrl()).toBe('/3');
- await Promise.all([pr1, pr2, pr3]);
- expect(getCurrentUrl()).toBe('/3');
- });
-
it('flush should take priority over regular replace behaviour', async () => {
const pr1 = urlControls.updateAsync(() => '/1', true);
const pr2 = urlControls.updateAsync(() => '/2', false);
@@ -174,6 +164,48 @@ describe('kbn_url_storage', () => {
await Promise.all([pr1, pr2, pr3]);
expect(getCurrentUrl()).toBe('/');
});
+
+ it('should retrieve pending url ', async () => {
+ const pr1 = urlControls.updateAsync(() => '/1', true);
+ const pr2 = urlControls.updateAsync(() => '/2', false);
+ const pr3 = urlControls.updateAsync(() => '/3', true);
+ expect(urlControls.getPendingUrl()).toEqual('/3');
+ expect(getCurrentUrl()).toBe('/');
+ await Promise.all([pr1, pr2, pr3]);
+ expect(getCurrentUrl()).toBe('/3');
+
+ expect(urlControls.getPendingUrl()).toBeUndefined();
+ });
+ });
+
+ describe('urlControls - browser history integration', () => {
+ let history: History;
+ let urlControls: IKbnUrlControls;
+ beforeEach(() => {
+ history = createBrowserHistory();
+ urlControls = createKbnUrlControls(history);
+ urlControls.update('/', true);
+ });
+
+ const getCurrentUrl = () => window.location.href;
+
+ it('should flush async url updates', async () => {
+ const pr1 = urlControls.updateAsync(() => '/1', false);
+ const pr2 = urlControls.updateAsync(() => '/2', false);
+ const pr3 = urlControls.updateAsync(() => '/3', false);
+ expect(getCurrentUrl()).toBe('http://localhost/');
+ expect(urlControls.flush()).toBe('http://localhost/3');
+ expect(getCurrentUrl()).toBe('http://localhost/3');
+ await Promise.all([pr1, pr2, pr3]);
+ expect(getCurrentUrl()).toBe('http://localhost/3');
+ });
+
+ it('flush() should return undefined, if no url updates happened', () => {
+ expect(urlControls.flush()).toBeUndefined();
+ urlControls.updateAsync(() => 'http://localhost/1', false);
+ urlControls.updateAsync(() => 'http://localhost/', false);
+ expect(urlControls.flush()).toBeUndefined();
+ });
});
describe('getRelativeToHistoryPath', () => {
diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts
index 03c136ea3d092..1dd204e717213 100644
--- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts
+++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts
@@ -107,25 +107,34 @@ export interface IKbnUrlControls {
listen: (cb: () => void) => () => void;
/**
- * Updates url synchronously
+ * Updates url synchronously, if needed
+ * skips the update and returns undefined in case when trying to update to current url
+ * otherwise returns new url
+ *
* @param url - url to update to
* @param replace - use replace instead of push
*/
- update: (url: string, replace: boolean) => string;
+ update: (url: string, replace: boolean) => string | undefined;
/**
* Schedules url update to next microtask,
* Useful to batch sync changes to url to cause only one browser history update
* @param updater - fn which receives current url and should return next url to update to
* @param replace - use replace instead of push
+ *
*/
- updateAsync: (updater: UrlUpdaterFnType, replace?: boolean) => Promise;
+ updateAsync: (updater: UrlUpdaterFnType, replace?: boolean) => Promise;
/**
- * Synchronously flushes scheduled url updates
+ * If there is a pending url update - returns url that is scheduled for update
+ */
+ getPendingUrl: () => string | undefined;
+
+ /**
+ * Synchronously flushes scheduled url updates. Returns new flushed url, if there was an update. Otherwise - undefined.
* @param replace - if replace passed in, then uses it instead of push. Otherwise push or replace is picked depending on updateQueue
*/
- flush: (replace?: boolean) => string;
+ flush: (replace?: boolean) => string | undefined;
/**
* Cancels any pending url updates
@@ -143,9 +152,9 @@ export const createKbnUrlControls = (
// if any call in a queue asked to push, then we should push
let shouldReplace = true;
- function updateUrl(newUrl: string, replace = false): string {
+ function updateUrl(newUrl: string, replace = false): string | undefined {
const currentUrl = getCurrentUrl();
- if (newUrl === currentUrl) return currentUrl; // skip update
+ if (newUrl === currentUrl) return undefined; // skip update
const historyPath = getRelativeToHistoryPath(newUrl, history);
@@ -166,15 +175,22 @@ export const createKbnUrlControls = (
// runs scheduled url updates
function flush(replace = shouldReplace) {
- if (updateQueue.length === 0) return getCurrentUrl();
- const resultUrl = updateQueue.reduce((url, nextUpdate) => nextUpdate(url), getCurrentUrl());
+ const nextUrl = getPendingUrl();
- cleanUp();
+ if (!nextUrl) return;
- const newUrl = updateUrl(resultUrl, replace);
+ cleanUp();
+ const newUrl = updateUrl(nextUrl, replace);
return newUrl;
}
+ function getPendingUrl() {
+ if (updateQueue.length === 0) return undefined;
+ const resultUrl = updateQueue.reduce((url, nextUpdate) => nextUpdate(url), getCurrentUrl());
+
+ return resultUrl;
+ }
+
return {
listen: (cb: () => void) =>
history.listen(() => {
@@ -199,6 +215,9 @@ export const createKbnUrlControls = (
cancel: () => {
cleanUp();
},
+ getPendingUrl: () => {
+ return getPendingUrl();
+ },
};
};
diff --git a/src/legacy/ui/public/state_management/utils/diff_object.test.ts b/src/plugins/kibana_utils/public/state_management/utils/diff_object.test.ts
similarity index 100%
rename from src/legacy/ui/public/state_management/utils/diff_object.test.ts
rename to src/plugins/kibana_utils/public/state_management/utils/diff_object.test.ts
diff --git a/src/legacy/ui/public/state_management/utils/diff_object.ts b/src/plugins/kibana_utils/public/state_management/utils/diff_object.ts
similarity index 100%
rename from src/legacy/ui/public/state_management/utils/diff_object.ts
rename to src/plugins/kibana_utils/public/state_management/utils/diff_object.ts
diff --git a/src/plugins/kibana_utils/public/state_sync/state_sync.test.ts b/src/plugins/kibana_utils/public/state_sync/state_sync.test.ts
index 08ad1551420d2..17f41483a0a21 100644
--- a/src/plugins/kibana_utils/public/state_sync/state_sync.test.ts
+++ b/src/plugins/kibana_utils/public/state_sync/state_sync.test.ts
@@ -291,6 +291,42 @@ describe('state_sync', () => {
stop();
});
+
+ it("should preserve reference to unchanged state slices if them didn't change", async () => {
+ const otherUnchangedSlice = { a: 'test' };
+ const oldState = {
+ todos: container.get().todos,
+ otherUnchangedSlice,
+ };
+ container.set(oldState as any);
+
+ const { stop, start } = syncStates([
+ {
+ stateContainer: withDefaultState(container, defaultState),
+ storageKey: key,
+ stateStorage: urlSyncStrategy,
+ },
+ ]);
+ await urlSyncStrategy.set('_s', container.get());
+ expect(getCurrentUrl()).toMatchInlineSnapshot(
+ `"/#?_s=(otherUnchangedSlice:(a:test),todos:!((completed:!f,id:0,text:'Learning%20state%20containers')))"`
+ );
+ start();
+
+ history.replace(
+ "/#?_s=(otherUnchangedSlice:(a:test),todos:!((completed:!t,id:0,text:'Learning%20state%20containers')))"
+ );
+
+ const newState = container.get();
+ expect(newState.todos).toEqual([
+ { id: 0, text: 'Learning state containers', completed: true },
+ ]);
+
+ // reference to unchanged slice is preserved
+ expect((newState as any).otherUnchangedSlice).toBe(otherUnchangedSlice);
+
+ stop();
+ });
});
});
diff --git a/src/plugins/kibana_utils/public/state_sync/state_sync.ts b/src/plugins/kibana_utils/public/state_sync/state_sync.ts
index 9c1116e5da531..28d133829e07c 100644
--- a/src/plugins/kibana_utils/public/state_sync/state_sync.ts
+++ b/src/plugins/kibana_utils/public/state_sync/state_sync.ts
@@ -24,6 +24,7 @@ import { IStateSyncConfig } from './types';
import { IStateStorage } from './state_sync_state_storage';
import { distinctUntilChangedWithInitialValue } from '../../common';
import { BaseState } from '../state_containers';
+import { applyDiff } from '../state_management/utils/diff_object';
/**
* Utility for syncing application state wrapped in state container
@@ -100,7 +101,18 @@ export function syncState<
const updateState = () => {
const newState = stateStorage.get(storageKey);
const oldState = stateContainer.get();
- if (!defaultComparator(newState, oldState)) {
+ if (newState) {
+ // apply only real differences to new state
+ const mergedState = { ...oldState } as State;
+ // merges into 'mergedState' all differences from newState,
+ // but leaves references if they are deeply the same
+ const diff = applyDiff(mergedState, newState);
+
+ if (diff.keys.length > 0) {
+ stateContainer.set(mergedState);
+ }
+ } else if (oldState !== newState) {
+ // empty new state case
stateContainer.set(newState);
}
};
diff --git a/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.test.ts b/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.test.ts
index 826122176e061..cc3f1df7c1e00 100644
--- a/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.test.ts
+++ b/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.test.ts
@@ -46,9 +46,11 @@ describe('KbnUrlStateStorage', () => {
const key = '_s';
urlStateStorage.set(key, state);
expect(getCurrentUrl()).toMatchInlineSnapshot(`"/"`);
- urlStateStorage.flush();
+ expect(urlStateStorage.flush()).toBe(true);
expect(getCurrentUrl()).toMatchInlineSnapshot(`"/#?_s=(ok:1,test:test)"`);
expect(urlStateStorage.get(key)).toEqual(state);
+
+ expect(urlStateStorage.flush()).toBe(false); // nothing to flush, not update
});
it('should cancel url updates', async () => {
@@ -62,6 +64,19 @@ describe('KbnUrlStateStorage', () => {
expect(urlStateStorage.get(key)).toEqual(null);
});
+ it('should cancel url updates if synchronously returned to the same state', async () => {
+ const state1 = { test: 'test', ok: 1 };
+ const state2 = { test: 'test', ok: 2 };
+ const key = '_s';
+ const pr1 = urlStateStorage.set(key, state1);
+ await pr1;
+ const historyLength = history.length;
+ const pr2 = urlStateStorage.set(key, state2);
+ const pr3 = urlStateStorage.set(key, state1);
+ await Promise.all([pr2, pr3]);
+ expect(history.length).toBe(historyLength);
+ });
+
it('should notify about url changes', async () => {
expect(urlStateStorage.change$).toBeDefined();
const key = '_s';
diff --git a/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.ts b/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.ts
index 245006349ad55..082eaa5095ab9 100644
--- a/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.ts
+++ b/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.ts
@@ -28,7 +28,11 @@ import {
} from '../../state_management/url';
export interface IKbnUrlStateStorage extends IStateStorage {
- set: (key: string, state: State, opts?: { replace: boolean }) => Promise;
+ set: (
+ key: string,
+ state: State,
+ opts?: { replace: boolean }
+ ) => Promise;
get: (key: string) => State | null;
change$: (key: string) => Observable;
@@ -36,7 +40,8 @@ export interface IKbnUrlStateStorage extends IStateStorage {
cancel: () => void;
// synchronously runs any pending url updates
- flush: (opts?: { replace?: boolean }) => void;
+ // returned boolean indicates if change occurred
+ flush: (opts?: { replace?: boolean }) => boolean;
}
/**
@@ -60,7 +65,11 @@ export const createKbnUrlStateStorage = (
replace
);
},
- get: key => getStateFromKbnUrl(key),
+ get: key => {
+ // if there is a pending url update, then state will be extracted from that pending url,
+ // otherwise current url will be used to retrieve state from
+ return getStateFromKbnUrl(key, url.getPendingUrl());
+ },
change$: (key: string) =>
new Observable(observer => {
const unlisten = url.listen(() => {
@@ -75,7 +84,7 @@ export const createKbnUrlStateStorage = (
share()
),
flush: ({ replace = false }: { replace?: boolean } = {}) => {
- url.flush(replace);
+ return !!url.flush(replace);
},
cancel() {
url.cancel();
diff --git a/test/functional/apps/dashboard/dashboard_clone.js b/test/functional/apps/dashboard/dashboard_clone.js
index f5485c1db206e..8b7f6ba6a34dd 100644
--- a/test/functional/apps/dashboard/dashboard_clone.js
+++ b/test/functional/apps/dashboard/dashboard_clone.js
@@ -37,7 +37,7 @@ export default function({ getService, getPageObjects }) {
await PageObjects.dashboard.addVisualizations(
PageObjects.dashboard.getTestVisualizationNames()
);
- await PageObjects.dashboard.enterDashboardTitleAndClickSave(dashboardName);
+ await PageObjects.dashboard.saveDashboard(dashboardName);
await PageObjects.dashboard.clickClone();
await PageObjects.dashboard.confirmClone();
From 9d3d3cdc67e692c90d3bc959984114f007156615 Mon Sep 17 00:00:00 2001
From: Aleh Zasypkin
Date: Tue, 21 Jan 2020 13:40:39 +0100
Subject: [PATCH 003/187] Migrate Management views to Kibana Platform plugin
(#53880)
---
.../saved_objects_client.test.ts | 19 -
.../saved_objects/saved_objects_client.ts | 6 +-
src/plugins/management/public/index.ts | 7 +-
.../legacy/plugins/security/common/model.ts | 28 -
x-pack/legacy/plugins/security/index.d.ts | 2 +-
x-pack/legacy/plugins/security/index.js | 3 -
.../security/public/documentation_links.js | 16 -
.../plugins/security/public/images/logout.svg | 3 -
.../plugins/security/public/images/person.svg | 3 -
.../legacy/plugins/security/public/index.scss | 3 +
.../security/public/lib/__tests__/util.js | 49 --
.../legacy/plugins/security/public/lib/api.ts | 56 --
.../security/public/lib/api_keys_api.ts | 47 --
.../plugins/security/public/lib/role_utils.ts | 58 --
.../plugins/security/public/lib/roles_api.ts | 25 -
.../public/lib/transform_role_for_save.ts | 41 -
.../plugins/security/public/lib/util.js | 19 -
.../security/public/objects/lib/get_fields.ts | 15 -
.../security/public/objects/lib/roles.ts | 21 -
.../security/public/register_feature.js | 29 -
.../public/services/shield_indices.js | 18 -
.../security/public/services/shield_role.js | 30 -
.../plugins/security/public/views/_index.scss | 3 -
.../public/views/account/account.html | 1 -
.../security/public/views/account/account.js | 27 +-
.../security/public/views/login/_index.scss | 3 +-
.../public/views/login/components/_index.scss | 1 +
.../basic_login_form.test.tsx | 2 +-
.../basic_login_form/basic_login_form.tsx | 2 +-
.../login/components/login_page/_index.scss | 1 +
.../login_page/_login_page.scss} | 5 -
.../components/login_page/login_page.test.tsx | 2 +-
.../components/login_page/login_page.tsx | 2 +-
.../security/public/views/login/login.html | 1 -
.../security/public/views/login/login.tsx | 8 +-
.../views/login}/login_state.ts | 0
.../login/parse_next.test.ts} | 47 +-
.../public/{lib => views/login}/parse_next.ts | 0
.../public/views/management/_index.scss | 4 -
.../management/api_keys_grid/api_keys.html | 3 -
.../management/api_keys_grid/api_keys.js | 35 -
.../public/views/management/breadcrumbs.ts | 115 ---
.../_change_password_form.scss | 17 -
.../change_password_form/_index.scss | 1 -
.../change_password_form.html | 141 ----
.../change_password_form.js | 48 --
.../views/management/edit_role/_index.scss | 1 -
.../edit_role/components/_index.scss | 9 -
.../components/edit_role_page.test.tsx | 716 ------------------
.../edit_role/components/edit_role_page.tsx | 409 ----------
.../es/elasticsearch_privileges.test.tsx | 96 ---
.../views/management/edit_role/edit_role.html | 3 -
.../views/management/edit_role/index.js | 176 -----
.../views/management/edit_user/_index.scss | 1 -
.../views/management/edit_user/edit_user.html | 3 -
.../views/management/edit_user/edit_user.js | 58 --
.../public/views/management/management.js | 134 ----
.../password_form/password_form.html | 53 --
.../management/password_form/password_form.js | 24 -
.../edit_role_mapping/_index.scss | 1 -
.../edit_role_mapping_page.test.tsx | 341 ---------
.../edit_role_mapping/edit_role_mapping.html | 3 -
.../role_mappings/edit_role_mapping/index.tsx | 45 --
.../role_mappings_grid_page.test.tsx | 182 -----
.../role_mappings_grid/index.tsx | 40 -
.../role_mappings_grid/role_mappings.html | 3 -
.../views/management/roles_grid/roles.html | 3 -
.../views/management/roles_grid/roles.js | 35 -
.../views/management/users_grid/users.html | 3 -
.../views/management/users_grid/users.js | 47 --
.../overwritten_session.tsx | 3 +-
x-pack/plugins/security/common/model/index.ts | 11 +-
.../security/common/model/role.test.ts} | 9 +-
x-pack/plugins/security/common/model/role.ts | 51 ++
x-pack/plugins/security/kibana.json | 3 +-
x-pack/plugins/security/public/_index.scss | 2 +
.../account_management_page.test.tsx | 37 +-
.../account_management_page.tsx | 18 +-
.../change_password/change_password.tsx | 19 +-
.../change_password/index.ts | 0
.../public/account_management}/index.ts | 0
.../personal_info/index.ts | 0
.../personal_info/personal_info.tsx | 11 +-
.../security/public/management/_index.scss | 3 +
.../api_keys/api_keys_api_client.mock.ts | 13 +
.../api_keys/api_keys_api_client.test.ts | 86 +++
.../api_keys/api_keys_api_client.ts | 42 +
.../api_keys_grid_page.test.tsx.snap | 20 +-
.../api_keys_grid_page.test.tsx | 124 +--
.../api_keys_grid}/api_keys_grid_page.tsx | 50 +-
.../empty_prompt/empty_prompt.tsx | 7 +-
.../api_keys_grid}/empty_prompt/index.ts | 0
.../api_keys/api_keys_grid}/index.ts | 2 +-
.../invalidate_provider/index.ts | 0
.../invalidate_provider.tsx | 21 +-
.../api_keys_grid}/not_enabled/index.ts | 0
.../not_enabled/not_enabled.tsx | 10 +-
.../api_keys_grid}/permission_denied/index.ts | 0
.../permission_denied/permission_denied.tsx | 0
.../api_keys/api_keys_management_app.test.tsx | 54 ++
.../api_keys/api_keys_management_app.tsx | 58 ++
.../api_keys}/documentation_links.ts | 16 +-
.../public/management/api_keys/index.mock.ts | 7 +
.../public/management/api_keys/index.ts | 7 +
.../security/public/management/index.ts | 8 +
.../management/management_service.test.ts | 226 ++++++
.../public/management/management_service.ts | 95 +++
.../public}/management/management_urls.ts | 9 +-
.../management/role_mappings/_index.scss | 1 +
.../delete_provider/delete_provider.test.tsx | 152 ++--
.../delete_provider/delete_provider.tsx | 21 +-
.../components/delete_provider/index.ts | 0
.../role_mappings/components/index.ts | 0
.../components/no_compatible_realms/index.ts | 0
.../no_compatible_realms.tsx | 10 +-
.../components/permission_denied/index.ts | 0
.../permission_denied/permission_denied.tsx | 0
.../components/section_loading/index.ts | 0
.../section_loading/section_loading.test.tsx | 0
.../section_loading/section_loading.tsx | 0
.../role_mappings}/documentation_links.ts | 12 +-
.../edit_role_mapping/_index.scss | 1 +
.../edit_role_mapping_page.test.tsx | 399 ++++++++++
.../edit_role_mapping_page.tsx | 44 +-
.../role_mappings/edit_role_mapping}/index.ts | 0
.../mapping_info_panel/index.ts | 0
.../mapping_info_panel.test.tsx | 36 +-
.../mapping_info_panel/mapping_info_panel.tsx | 13 +-
.../add_role_template_button.test.tsx | 0
.../add_role_template_button.tsx | 0
.../role_selector/index.tsx | 0
.../role_selector/role_selector.test.tsx | 25 +-
.../role_selector/role_selector.tsx | 7 +-
.../role_template_editor.test.tsx | 0
.../role_selector/role_template_editor.tsx | 4 +-
.../role_template_type_select.tsx | 4 +-
.../rule_editor_panel/_index.scss | 1 +
.../_rule_editor_group.scss} | 0
.../add_rule_button.test.tsx | 2 +-
.../rule_editor_panel/add_rule_button.tsx | 2 +-
.../field_rule_editor.test.tsx | 2 +-
.../rule_editor_panel/field_rule_editor.tsx | 2 +-
.../rule_editor_panel/index.tsx | 0
.../json_rule_editor.test.tsx | 10 +-
.../rule_editor_panel/json_rule_editor.tsx | 7 +-
.../rule_editor_panel.test.tsx | 9 +-
.../rule_editor_panel/rule_editor_panel.tsx | 14 +-
.../rule_group_editor.test.tsx | 2 +-
.../rule_editor_panel/rule_group_editor.tsx | 4 +-
.../rule_editor_panel/rule_group_title.tsx | 9 +-
.../visual_rule_editor.test.tsx | 2 +-
.../rule_editor_panel/visual_rule_editor.tsx | 6 +-
.../services/is_rule_group.ts | 0
.../services/role_mapping_constants.ts | 0
.../services/role_mapping_validation.test.ts | 2 +-
.../services/role_mapping_validation.ts | 2 +-
.../services/role_template_type.test.ts | 2 +-
.../services/role_template_type.ts | 2 +-
.../management/role_mappings/index.mock.ts | 7 +
.../public/management/role_mappings/index.ts | 7 +
.../__snapshots__/rule_builder.test.ts.snap | 0
.../role_mappings/model/all_rule.test.ts | 0
.../role_mappings/model/all_rule.ts | 0
.../role_mappings/model/any_rule.test.ts | 0
.../role_mappings/model/any_rule.ts | 0
.../model/except_all_rule.test.ts | 0
.../role_mappings/model/except_all_rule.ts | 0
.../model/except_any_rule.test.ts | 0
.../role_mappings/model/except_any_rule.ts | 0
.../role_mappings/model/field_rule.test.ts | 0
.../role_mappings/model/field_rule.ts | 0
.../management/role_mappings/model/index.ts | 0
.../management/role_mappings/model/rule.ts | 0
.../role_mappings/model/rule_builder.test.ts | 2 +-
.../role_mappings/model/rule_builder.ts | 2 +-
.../role_mappings/model/rule_builder_error.ts | 0
.../role_mappings/model/rule_group.ts | 2 +-
.../role_mappings_api_client.mock.ts | 15 +
.../role_mappings_api_client.ts} | 8 +-
.../create_role_mapping_button.tsx | 2 +-
.../create_role_mapping_button/index.ts | 0
.../empty_prompt/empty_prompt.tsx | 0
.../role_mappings_grid}/empty_prompt/index.ts | 0
.../role_mappings_grid}/index.ts | 0
.../role_mappings_grid_page.test.tsx | 219 ++++++
.../role_mappings_grid_page.tsx | 29 +-
.../role_mappings_management_app.test.tsx | 127 ++++
.../role_mappings_management_app.tsx | 104 +++
.../public/management/roles/_index.scss | 1 +
.../management/roles/documentation_links.ts | 27 +
.../__snapshots__/validate_role.test.ts.snap | 0
.../management/roles/edit_role/_index.scss | 3 +
.../collapsible_panel.test.tsx.snap | 0
.../collapsible_panel/_collapsible_panel.scss | 0
.../edit_role/collapsible_panel/_index.scss | 1 +
.../collapsible_panel.test.tsx | 0
.../collapsible_panel/collapsible_panel.tsx | 1 -
.../edit_role}/collapsible_panel/index.ts | 0
.../edit_role}/delete_role_button.test.tsx | 6 +-
.../roles/edit_role}/delete_role_button.tsx | 8 +-
.../roles/edit_role/edit_role_page.test.tsx | 552 ++++++++++++++
.../roles/edit_role/edit_role_page.tsx | 594 +++++++++++++++
.../management/roles/edit_role}/index.ts | 0
.../roles/edit_role}/privilege_utils.test.ts | 0
.../roles/edit_role}/privilege_utils.ts | 2 +-
.../roles/edit_role/privileges/_index.scss | 2 +
.../privileges/_privilege_feature_icon.scss | 4 +
.../cluster_privileges.test.tsx.snap | 0
.../elasticsearch_privileges.test.tsx.snap | 24 +-
.../index_privilege_form.test.tsx.snap | 0
.../index_privileges.test.tsx.snap | 0
.../privileges/es/cluster_privileges.test.tsx | 2 +-
.../privileges/es/cluster_privileges.tsx | 3 +-
.../es/elasticsearch_privileges.test.tsx | 67 ++
.../es/elasticsearch_privileges.tsx | 34 +-
.../es/index_privilege_form.test.tsx | 2 +-
.../privileges/es/index_privilege_form.tsx | 7 +-
.../privileges/es/index_privileges.test.tsx | 27 +-
.../privileges/es/index_privileges.tsx | 32 +-
.../roles/edit_role}/privileges/index.ts | 0
.../kibana_privileges_region.test.tsx.snap | 0
.../edit_role/privileges/kibana/_index.scss | 2 +
.../edit_role/privileges/kibana}/constants.ts | 0
.../__snapshots__/feature_table.test.tsx.snap | 0
.../_change_all_privileges.scss} | 0
.../kibana/feature_table/_index.scss | 1 +
.../feature_table/change_all_privileges.tsx | 0
.../feature_table/feature_table.test.tsx | 7 +-
.../kibana/feature_table/feature_table.tsx | 32 +-
.../privileges/kibana/feature_table/index.ts | 0
.../__fixtures__/build_role.ts | 2 +-
.../__fixtures__/common_allowed_privileges.ts | 0
.../default_privilege_definition.ts | 2 +-
.../__fixtures__/index.ts | 0
.../kibana_privilege_calculator/index.ts | 0
...bana_allowed_privileges_calculator.test.ts | 2 +-
.../kibana_allowed_privileges_calculator.ts | 11 +-
.../kibana_base_privilege_calculator.test.ts | 6 +-
.../kibana_base_privilege_calculator.ts | 9 +-
...ibana_feature_privilege_calculator.test.ts | 6 +-
.../kibana_feature_privilege_calculator.ts | 13 +-
.../kibana_privilege_calculator.test.ts | 4 +-
.../kibana_privilege_calculator.ts | 5 +-
.../kibana_privilege_calculator_types.ts | 0
.../kibana_privileges_calculator_factory.ts | 12 +-
.../kibana/kibana_privileges_region.test.tsx | 4 +-
.../kibana/kibana_privileges_region.tsx | 17 +-
.../simple_privilege_section.test.tsx.snap | 0
.../kibana/simple_privilege_section/index.ts | 0
.../privilege_selector.tsx | 2 +-
.../simple_privilege_section.test.tsx | 8 +-
.../simple_privilege_section.tsx | 21 +-
.../unsupported_space_privileges_warning.tsx | 0
.../__fixtures__/index.ts | 0
.../__fixtures__/raw_kibana_privileges.ts | 2 +-
.../privilege_display.test.tsx.snap | 0
.../privilege_space_form.test.tsx.snap | 102 ---
...pace_aware_privilege_section.test.tsx.snap | 0
.../space_aware_privilege_section/_index.scss | 0
.../_privilege_matrix.scss | 2 +-
.../space_aware_privilege_section/index.ts | 0
.../privilege_display.test.tsx | 2 +-
.../privilege_display.tsx | 7 +-
.../privilege_matrix.test.tsx | 10 +-
.../privilege_matrix.tsx | 17 +-
.../privilege_space_form.test.tsx | 4 +-
.../privilege_space_form.tsx | 14 +-
.../privilege_space_table.test.tsx | 4 +-
.../privilege_space_table.tsx | 15 +-
.../space_aware_privilege_section.test.tsx | 6 +-
.../space_aware_privilege_section.tsx | 15 +-
.../space_selector.tsx | 4 +-
.../kibana/transform_error_section/index.ts | 0
.../transform_error_section.tsx | 0
.../edit_role}/reserved_role_badge.test.tsx | 2 +-
.../roles/edit_role}/reserved_role_badge.tsx | 3 +-
.../edit_role/spaces_popover_list/_index.scss | 1 +
.../_spaces_popover_list.scss | 0
.../edit_role}/spaces_popover_list/index.ts | 0
.../spaces_popover_list.tsx | 7 +-
.../roles/edit_role}/validate_role.test.ts | 2 +-
.../roles/edit_role}/validate_role.ts | 2 +-
.../public/management/roles/index.mock.ts | 9 +
.../security/public/management/roles/index.ts | 8 +
.../roles/indices_api_client.mock.ts | 11 +
.../management/roles/indices_api_client.ts | 18 +
.../roles/privileges_api_client.mock.ts | 12 +
.../management/roles/privileges_api_client.ts | 22 +
.../management/roles/roles_api_client.mock.ts | 14 +
.../roles/roles_api_client.test.ts} | 73 +-
.../management/roles/roles_api_client.ts | 62 ++
.../roles_grid_page.test.tsx.snap | 0
.../confirm_delete/confirm_delete.tsx | 57 +-
.../roles/roles_grid}/confirm_delete/index.ts | 0
.../management/roles/roles_grid}/index.ts | 0
.../roles_grid}/permission_denied/index.ts | 0
.../permission_denied/permission_denied.tsx | 0
.../roles_grid}/roles_grid_page.test.tsx | 89 ++-
.../roles/roles_grid}/roles_grid_page.tsx | 89 +--
.../roles/roles_management_app.test.tsx | 160 ++++
.../management/roles/roles_management_app.tsx | 116 +++
.../public/management/users/_index.scss | 1 +
.../change_password_form.test.tsx | 24 +-
.../change_password_form.tsx | 16 +-
.../components}/change_password_form/index.ts | 0
.../confirm_delete_users.test.tsx} | 53 +-
.../confirm_delete_users.tsx} | 81 +-
.../components/confirm_delete_users}/index.ts | 2 +-
.../management/users/components/index.ts | 8 +
.../users/edit_user/_edit_user_page.scss} | 0
.../management/users/edit_user/_index.scss | 1 +
.../users/edit_user}/edit_user_page.test.tsx | 55 +-
.../users/edit_user}/edit_user_page.tsx | 175 ++---
.../management/users/edit_user}/index.ts | 0
.../users/edit_user}/validate_user.test.ts | 2 +-
.../users/edit_user}/validate_user.ts | 2 +-
.../public/management/users/index.mock.ts} | 2 +-
.../public/management/users}/index.ts | 5 +-
.../management/users/user_api_client.mock.ts | 15 +
.../management/users/user_api_client.ts | 45 ++
.../management/users/users_grid}/index.ts | 2 +-
.../users_grid/users_grid_page.test.tsx} | 34 +-
.../users/users_grid/users_grid_page.tsx} | 52 +-
.../users/users_management_app.test.tsx | 131 ++++
.../management/users/users_management_app.tsx | 94 +++
x-pack/plugins/security/public/plugin.ts | 74 --
x-pack/plugins/security/public/plugin.tsx | 147 ++++
.../server/routes/role_mapping/get.ts | 2 +-
x-pack/plugins/spaces/common/index.ts | 2 +-
.../translations/translations/ja-JP.json | 15 -
.../translations/translations/zh-CN.json | 15 -
.../functional/apps/security/management.js | 2 +-
.../functional/apps/security/role_mappings.ts | 2 +-
333 files changed, 4797 insertions(+), 4496 deletions(-)
delete mode 100644 x-pack/legacy/plugins/security/common/model.ts
delete mode 100644 x-pack/legacy/plugins/security/public/documentation_links.js
delete mode 100644 x-pack/legacy/plugins/security/public/images/logout.svg
delete mode 100644 x-pack/legacy/plugins/security/public/images/person.svg
delete mode 100644 x-pack/legacy/plugins/security/public/lib/__tests__/util.js
delete mode 100644 x-pack/legacy/plugins/security/public/lib/api.ts
delete mode 100644 x-pack/legacy/plugins/security/public/lib/api_keys_api.ts
delete mode 100644 x-pack/legacy/plugins/security/public/lib/role_utils.ts
delete mode 100644 x-pack/legacy/plugins/security/public/lib/roles_api.ts
delete mode 100644 x-pack/legacy/plugins/security/public/lib/transform_role_for_save.ts
delete mode 100644 x-pack/legacy/plugins/security/public/lib/util.js
delete mode 100644 x-pack/legacy/plugins/security/public/objects/lib/get_fields.ts
delete mode 100644 x-pack/legacy/plugins/security/public/objects/lib/roles.ts
delete mode 100644 x-pack/legacy/plugins/security/public/register_feature.js
delete mode 100644 x-pack/legacy/plugins/security/public/services/shield_indices.js
delete mode 100644 x-pack/legacy/plugins/security/public/services/shield_role.js
delete mode 100644 x-pack/legacy/plugins/security/public/views/account/account.html
create mode 100644 x-pack/legacy/plugins/security/public/views/login/components/_index.scss
create mode 100644 x-pack/legacy/plugins/security/public/views/login/components/login_page/_index.scss
rename x-pack/legacy/plugins/security/public/views/login/{_login.scss => components/login_page/_login_page.scss} (88%)
delete mode 100644 x-pack/legacy/plugins/security/public/views/login/login.html
rename x-pack/legacy/plugins/security/{common => public/views/login}/login_state.ts (100%)
rename x-pack/legacy/plugins/security/public/{lib/__tests__/parse_next.js => views/login/parse_next.test.ts} (80%)
rename x-pack/legacy/plugins/security/public/{lib => views/login}/parse_next.ts (100%)
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/_index.scss
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/api_keys_grid/api_keys.html
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/api_keys_grid/api_keys.js
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/breadcrumbs.ts
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/change_password_form/_change_password_form.scss
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/change_password_form/_index.scss
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/change_password_form/change_password_form.html
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/change_password_form/change_password_form.js
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/edit_role/_index.scss
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/edit_role/components/_index.scss
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.test.tsx
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.tsx
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/elasticsearch_privileges.test.tsx
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/edit_role/edit_role.html
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/edit_role/index.js
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/edit_user/_index.scss
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/edit_user/edit_user.html
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/edit_user/edit_user.js
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/management.js
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/password_form/password_form.html
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/password_form/password_form.js
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/_index.scss
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/edit_role_mapping_page.test.tsx
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/edit_role_mapping.html
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/index.tsx
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/role_mappings_grid_page.test.tsx
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/index.tsx
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/role_mappings.html
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/roles_grid/roles.html
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/roles_grid/roles.js
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/users_grid/users.html
delete mode 100644 x-pack/legacy/plugins/security/public/views/management/users_grid/users.js
rename x-pack/{legacy/plugins/security/public/lib/role_utils.test.ts => plugins/security/common/model/role.test.ts} (96%)
create mode 100644 x-pack/plugins/security/public/_index.scss
rename x-pack/{legacy/plugins/security/public/views/account/components => plugins/security/public/account_management}/account_management_page.test.tsx (72%)
rename x-pack/{legacy/plugins/security/public/views/account/components => plugins/security/public/account_management}/account_management_page.tsx (62%)
rename x-pack/{legacy/plugins/security/public/views/account/components => plugins/security/public/account_management}/change_password/change_password.tsx (81%)
rename x-pack/{legacy/plugins/security/public/views/account/components => plugins/security/public/account_management}/change_password/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/account/components => plugins/security/public/account_management}/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/account/components => plugins/security/public/account_management}/personal_info/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/account/components => plugins/security/public/account_management}/personal_info/personal_info.tsx (89%)
create mode 100644 x-pack/plugins/security/public/management/_index.scss
create mode 100644 x-pack/plugins/security/public/management/api_keys/api_keys_api_client.mock.ts
create mode 100644 x-pack/plugins/security/public/management/api_keys/api_keys_api_client.test.ts
create mode 100644 x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts
rename x-pack/{legacy/plugins/security/public/views/management/api_keys_grid/components => plugins/security/public/management/api_keys/api_keys_grid}/__snapshots__/api_keys_grid_page.test.tsx.snap (91%)
rename x-pack/{legacy/plugins/security/public/views/management/api_keys_grid/components => plugins/security/public/management/api_keys/api_keys_grid}/api_keys_grid_page.test.tsx (65%)
rename x-pack/{legacy/plugins/security/public/views/management/api_keys_grid/components => plugins/security/public/management/api_keys/api_keys_grid}/api_keys_grid_page.tsx (90%)
rename x-pack/{legacy/plugins/security/public/views/management/api_keys_grid/components => plugins/security/public/management/api_keys/api_keys_grid}/empty_prompt/empty_prompt.tsx (89%)
rename x-pack/{legacy/plugins/security/public/views/management/api_keys_grid/components => plugins/security/public/management/api_keys/api_keys_grid}/empty_prompt/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/api_keys_grid/components => plugins/security/public/management/api_keys/api_keys_grid}/index.ts (81%)
rename x-pack/{legacy/plugins/security/public/views/management/api_keys_grid/components => plugins/security/public/management/api_keys/api_keys_grid}/invalidate_provider/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/api_keys_grid/components => plugins/security/public/management/api_keys/api_keys_grid}/invalidate_provider/invalidate_provider.tsx (91%)
rename x-pack/{legacy/plugins/security/public/views/management/api_keys_grid/components => plugins/security/public/management/api_keys/api_keys_grid}/not_enabled/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/api_keys_grid/components => plugins/security/public/management/api_keys/api_keys_grid}/not_enabled/not_enabled.tsx (78%)
rename x-pack/{legacy/plugins/security/public/views/management/api_keys_grid/components => plugins/security/public/management/api_keys/api_keys_grid}/permission_denied/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/api_keys_grid/components => plugins/security/public/management/api_keys/api_keys_grid}/permission_denied/permission_denied.tsx (100%)
create mode 100644 x-pack/plugins/security/public/management/api_keys/api_keys_management_app.test.tsx
create mode 100644 x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx
rename x-pack/{legacy/plugins/security/public/views/management/api_keys_grid/services => plugins/security/public/management/api_keys}/documentation_links.ts (51%)
create mode 100644 x-pack/plugins/security/public/management/api_keys/index.mock.ts
create mode 100644 x-pack/plugins/security/public/management/api_keys/index.ts
create mode 100644 x-pack/plugins/security/public/management/index.ts
create mode 100644 x-pack/plugins/security/public/management/management_service.test.ts
create mode 100644 x-pack/plugins/security/public/management/management_service.ts
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/management_urls.ts (74%)
create mode 100644 x-pack/plugins/security/public/management/role_mappings/_index.scss
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/components/delete_provider/delete_provider.test.tsx (64%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/components/delete_provider/delete_provider.tsx (93%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/components/delete_provider/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/components/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/components/no_compatible_realms/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/components/no_compatible_realms/no_compatible_realms.tsx (78%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/components/permission_denied/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/components/permission_denied/permission_denied.tsx (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/components/section_loading/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/components/section_loading/section_loading.test.tsx (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/components/section_loading/section_loading.tsx (100%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/services => plugins/security/public/management/role_mappings}/documentation_links.ts (70%)
create mode 100644 x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/_index.scss
create mode 100644 x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.test.tsx
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/edit_role_mapping_page.tsx (87%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/mapping_info_panel/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/mapping_info_panel/mapping_info_panel.test.tsx (82%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/mapping_info_panel/mapping_info_panel.tsx (95%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/role_selector/add_role_template_button.test.tsx (100%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/role_selector/add_role_template_button.tsx (100%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/role_selector/index.tsx (100%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/role_selector/role_selector.test.tsx (85%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/role_selector/role_selector.tsx (94%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/role_selector/role_template_editor.test.tsx (100%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/role_selector/role_template_editor.tsx (98%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/role_selector/role_template_type_select.tsx (92%)
create mode 100644 x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/_index.scss
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/_index.scss => plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/_rule_editor_group.scss} (100%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/rule_editor_panel/add_rule_button.test.tsx (97%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/rule_editor_panel/add_rule_button.tsx (97%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/rule_editor_panel/field_rule_editor.test.tsx (99%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/rule_editor_panel/field_rule_editor.tsx (99%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/rule_editor_panel/index.tsx (100%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/rule_editor_panel/json_rule_editor.test.tsx (89%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/rule_editor_panel/json_rule_editor.tsx (95%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/rule_editor_panel/rule_editor_panel.test.tsx (88%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/rule_editor_panel/rule_editor_panel.tsx (94%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/rule_editor_panel/rule_group_editor.test.tsx (99%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/rule_editor_panel/rule_group_editor.tsx (97%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/rule_editor_panel/rule_group_title.tsx (97%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/rule_editor_panel/visual_rule_editor.test.tsx (99%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components => plugins/security/public/management/role_mappings/edit_role_mapping}/rule_editor_panel/visual_rule_editor.tsx (95%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/edit_role_mapping/services/is_rule_group.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/edit_role_mapping/services/role_mapping_constants.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/edit_role_mapping/services/role_mapping_validation.test.ts (98%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/edit_role_mapping/services/role_mapping_validation.ts (97%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/edit_role_mapping/services/role_template_type.test.ts (96%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/edit_role_mapping/services/role_template_type.ts (96%)
create mode 100644 x-pack/plugins/security/public/management/role_mappings/index.mock.ts
create mode 100644 x-pack/plugins/security/public/management/role_mappings/index.ts
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/model/__snapshots__/rule_builder.test.ts.snap (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/model/all_rule.test.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/model/all_rule.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/model/any_rule.test.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/model/any_rule.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/model/except_all_rule.test.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/model/except_all_rule.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/model/except_any_rule.test.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/model/except_any_rule.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/model/field_rule.test.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/model/field_rule.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/model/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/model/rule.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/model/rule_builder.test.ts (99%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/model/rule_builder.ts (99%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/model/rule_builder_error.ts (100%)
rename x-pack/{legacy/plugins/security/public/views => plugins/security/public}/management/role_mappings/model/rule_group.ts (94%)
create mode 100644 x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.mock.ts
rename x-pack/{legacy/plugins/security/public/lib/role_mappings_api.ts => plugins/security/public/management/role_mappings/role_mappings_api_client.ts} (89%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components => plugins/security/public/management/role_mappings/role_mappings_grid}/create_role_mapping_button/create_role_mapping_button.tsx (90%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components => plugins/security/public/management/role_mappings/role_mappings_grid}/create_role_mapping_button/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components => plugins/security/public/management/role_mappings/role_mappings_grid}/empty_prompt/empty_prompt.tsx (100%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components => plugins/security/public/management/role_mappings/role_mappings_grid}/empty_prompt/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components => plugins/security/public/management/role_mappings/role_mappings_grid}/index.ts (100%)
create mode 100644 x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.test.tsx
rename x-pack/{legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components => plugins/security/public/management/role_mappings/role_mappings_grid}/role_mappings_grid_page.tsx (93%)
create mode 100644 x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.test.tsx
create mode 100644 x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx
create mode 100644 x-pack/plugins/security/public/management/roles/_index.scss
create mode 100644 x-pack/plugins/security/public/management/roles/documentation_links.ts
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/lib => plugins/security/public/management/roles/edit_role}/__snapshots__/validate_role.test.ts.snap (100%)
create mode 100644 x-pack/plugins/security/public/management/roles/edit_role/_index.scss
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/collapsible_panel/__snapshots__/collapsible_panel.test.tsx.snap (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/collapsible_panel/_collapsible_panel.scss (100%)
create mode 100644 x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/_index.scss
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/collapsible_panel/collapsible_panel.test.tsx (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/collapsible_panel/collapsible_panel.tsx (99%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/collapsible_panel/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/delete_role_button.test.tsx (94%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/delete_role_button.tsx (95%)
create mode 100644 x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx
create mode 100644 x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/roles/edit_role}/privilege_utils.test.ts (100%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/roles/edit_role}/privilege_utils.ts (93%)
create mode 100644 x-pack/plugins/security/public/management/roles/edit_role/privileges/_index.scss
create mode 100644 x-pack/plugins/security/public/management/roles/edit_role/privileges/_privilege_feature_icon.scss
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/es/__snapshots__/cluster_privileges.test.tsx.snap (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap (87%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/es/__snapshots__/index_privilege_form.test.tsx.snap (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/es/__snapshots__/index_privileges.test.tsx.snap (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/es/cluster_privileges.test.tsx (96%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/es/cluster_privileges.tsx (93%)
create mode 100644 x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.test.tsx
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/es/elasticsearch_privileges.tsx (89%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/es/index_privilege_form.test.tsx (99%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/es/index_privilege_form.tsx (98%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/es/index_privileges.test.tsx (74%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/es/index_privileges.tsx (84%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/__snapshots__/kibana_privileges_region.test.tsx.snap (100%)
create mode 100644 x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/_index.scss
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/lib => plugins/security/public/management/roles/edit_role/privileges/kibana}/constants.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/feature_table/__snapshots__/feature_table.test.tsx.snap (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/feature_table/_index.scss => plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/_change_all_privileges.scss} (100%)
create mode 100644 x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/_index.scss
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/feature_table/change_all_privileges.tsx (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/feature_table/feature_table.test.tsx (94%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/feature_table/feature_table.tsx (91%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/feature_table/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/roles/edit_role/privileges/kibana}/kibana_privilege_calculator/__fixtures__/build_role.ts (90%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/roles/edit_role/privileges/kibana}/kibana_privilege_calculator/__fixtures__/common_allowed_privileges.ts (100%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/roles/edit_role/privileges/kibana}/kibana_privilege_calculator/__fixtures__/default_privilege_definition.ts (94%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/roles/edit_role/privileges/kibana}/kibana_privilege_calculator/__fixtures__/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/roles/edit_role/privileges/kibana}/kibana_privilege_calculator/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/roles/edit_role/privileges/kibana}/kibana_privilege_calculator/kibana_allowed_privileges_calculator.test.ts (99%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/roles/edit_role/privileges/kibana}/kibana_privilege_calculator/kibana_allowed_privileges_calculator.ts (94%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/roles/edit_role/privileges/kibana}/kibana_privilege_calculator/kibana_base_privilege_calculator.test.ts (98%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/roles/edit_role/privileges/kibana}/kibana_privilege_calculator/kibana_base_privilege_calculator.ts (91%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/roles/edit_role/privileges/kibana}/kibana_privilege_calculator/kibana_feature_privilege_calculator.test.ts (99%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/roles/edit_role/privileges/kibana}/kibana_privilege_calculator/kibana_feature_privilege_calculator.ts (94%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/roles/edit_role/privileges/kibana}/kibana_privilege_calculator/kibana_privilege_calculator.test.ts (99%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/roles/edit_role/privileges/kibana}/kibana_privilege_calculator/kibana_privilege_calculator.ts (96%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/roles/edit_role/privileges/kibana}/kibana_privilege_calculator/kibana_privilege_calculator_types.ts (100%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/roles/edit_role/privileges/kibana}/kibana_privilege_calculator/kibana_privileges_calculator_factory.ts (90%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/kibana_privileges_region.test.tsx (95%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/kibana_privileges_region.tsx (79%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/simple_privilege_section/__snapshots__/simple_privilege_section.test.tsx.snap (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/simple_privilege_section/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/simple_privilege_section/privilege_selector.tsx (95%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx (95%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/simple_privilege_section/simple_privilege_section.tsx (94%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/simple_privilege_section/unsupported_space_privileges_warning.tsx (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/__fixtures__/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/__fixtures__/raw_kibana_privileges.ts (94%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/__snapshots__/privilege_display.test.tsx.snap (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/__snapshots__/privilege_space_form.test.tsx.snap (82%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/__snapshots__/space_aware_privilege_section.test.tsx.snap (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/_index.scss (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/_privilege_matrix.scss (87%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/privilege_display.test.tsx (96%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/privilege_display.tsx (96%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/privilege_matrix.test.tsx (89%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/privilege_matrix.tsx (93%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx (95%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx (96%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/privilege_space_table.test.tsx (99%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx (94%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.test.tsx (95%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx (93%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/space_aware_privilege_section/space_selector.tsx (93%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/transform_error_section/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/privileges/kibana/transform_error_section/transform_error_section.tsx (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/reserved_role_badge.test.tsx (96%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/reserved_role_badge.tsx (89%)
create mode 100644 x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/_index.scss
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/spaces_popover_list/_spaces_popover_list.scss (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/spaces_popover_list/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/components => plugins/security/public/management/roles/edit_role}/spaces_popover_list/spaces_popover_list.tsx (95%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/lib => plugins/security/public/management/roles/edit_role}/validate_role.test.ts (99%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_role/lib => plugins/security/public/management/roles/edit_role}/validate_role.ts (98%)
create mode 100644 x-pack/plugins/security/public/management/roles/index.mock.ts
create mode 100644 x-pack/plugins/security/public/management/roles/index.ts
create mode 100644 x-pack/plugins/security/public/management/roles/indices_api_client.mock.ts
create mode 100644 x-pack/plugins/security/public/management/roles/indices_api_client.ts
create mode 100644 x-pack/plugins/security/public/management/roles/privileges_api_client.mock.ts
create mode 100644 x-pack/plugins/security/public/management/roles/privileges_api_client.ts
create mode 100644 x-pack/plugins/security/public/management/roles/roles_api_client.mock.ts
rename x-pack/{legacy/plugins/security/public/lib/transform_role_for_save.test.ts => plugins/security/public/management/roles/roles_api_client.test.ts} (82%)
create mode 100644 x-pack/plugins/security/public/management/roles/roles_api_client.ts
rename x-pack/{legacy/plugins/security/public/views/management/roles_grid/components => plugins/security/public/management/roles/roles_grid}/__snapshots__/roles_grid_page.test.tsx.snap (100%)
rename x-pack/{legacy/plugins/security/public/views/management/roles_grid/components => plugins/security/public/management/roles/roles_grid}/confirm_delete/confirm_delete.tsx (72%)
rename x-pack/{legacy/plugins/security/public/views/management/roles_grid/components => plugins/security/public/management/roles/roles_grid}/confirm_delete/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/roles_grid/components => plugins/security/public/management/roles/roles_grid}/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/roles_grid/components => plugins/security/public/management/roles/roles_grid}/permission_denied/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/views/management/roles_grid/components => plugins/security/public/management/roles/roles_grid}/permission_denied/permission_denied.tsx (100%)
rename x-pack/{legacy/plugins/security/public/views/management/roles_grid/components => plugins/security/public/management/roles/roles_grid}/roles_grid_page.test.tsx (63%)
rename x-pack/{legacy/plugins/security/public/views/management/roles_grid/components => plugins/security/public/management/roles/roles_grid}/roles_grid_page.tsx (78%)
create mode 100644 x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx
create mode 100644 x-pack/plugins/security/public/management/roles/roles_management_app.tsx
create mode 100644 x-pack/plugins/security/public/management/users/_index.scss
rename x-pack/{legacy/plugins/security/public/components/management => plugins/security/public/management/users/components}/change_password_form/change_password_form.test.tsx (82%)
rename x-pack/{legacy/plugins/security/public/components/management => plugins/security/public/management/users/components}/change_password_form/change_password_form.tsx (96%)
rename x-pack/{legacy/plugins/security/public/components/management => plugins/security/public/management/users/components}/change_password_form/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/components/management/users/confirm_delete.test.tsx => plugins/security/public/management/users/components/confirm_delete_users/confirm_delete_users.test.tsx} (58%)
rename x-pack/{legacy/plugins/security/public/components/management/users/confirm_delete.tsx => plugins/security/public/management/users/components/confirm_delete_users/confirm_delete_users.tsx} (50%)
rename x-pack/{legacy/plugins/security/public/components/management/users => plugins/security/public/management/users/components/confirm_delete_users}/index.ts (79%)
create mode 100644 x-pack/plugins/security/public/management/users/components/index.ts
rename x-pack/{legacy/plugins/security/public/views/management/edit_user/_users.scss => plugins/security/public/management/users/edit_user/_edit_user_page.scss} (100%)
create mode 100644 x-pack/plugins/security/public/management/users/edit_user/_index.scss
rename x-pack/{legacy/plugins/security/public/views/management/edit_user/components => plugins/security/public/management/users/edit_user}/edit_user_page.test.tsx (70%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_user/components => plugins/security/public/management/users/edit_user}/edit_user_page.tsx (77%)
rename x-pack/{legacy/plugins/security/public/views/management/edit_user/components => plugins/security/public/management/users/edit_user}/index.ts (100%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/users/edit_user}/validate_user.test.ts (98%)
rename x-pack/{legacy/plugins/security/public/lib => plugins/security/public/management/users/edit_user}/validate_user.ts (98%)
rename x-pack/{legacy/plugins/security/public/views/management/index.js => plugins/security/public/management/users/index.mock.ts} (80%)
rename x-pack/{legacy/plugins/security/public/objects => plugins/security/public/management/users}/index.ts (68%)
create mode 100644 x-pack/plugins/security/public/management/users/user_api_client.mock.ts
create mode 100644 x-pack/plugins/security/public/management/users/user_api_client.ts
rename x-pack/{legacy/plugins/security/public/views/management/users_grid/components => plugins/security/public/management/users/users_grid}/index.ts (82%)
rename x-pack/{legacy/plugins/security/public/views/management/users_grid/components/users_list_page.test.tsx => plugins/security/public/management/users/users_grid/users_grid_page.test.tsx} (63%)
rename x-pack/{legacy/plugins/security/public/views/management/users_grid/components/users_list_page.tsx => plugins/security/public/management/users/users_grid/users_grid_page.tsx} (85%)
create mode 100644 x-pack/plugins/security/public/management/users/users_management_app.test.tsx
create mode 100644 x-pack/plugins/security/public/management/users/users_management_app.tsx
delete mode 100644 x-pack/plugins/security/public/plugin.ts
create mode 100644 x-pack/plugins/security/public/plugin.tsx
diff --git a/src/core/public/saved_objects/saved_objects_client.test.ts b/src/core/public/saved_objects/saved_objects_client.test.ts
index e633e00965c6a..0c34a16c68e99 100644
--- a/src/core/public/saved_objects/saved_objects_client.test.ts
+++ b/src/core/public/saved_objects/saved_objects_client.test.ts
@@ -448,23 +448,4 @@ describe('SavedObjectsClient', () => {
`);
});
});
-
- it('maintains backwards compatibility by transforming http.fetch errors to be compatible with kfetch errors', () => {
- const err = {
- response: { ok: false, redirected: false, status: 409, statusText: 'Conflict' },
- body: 'response body',
- };
- http.fetch.mockRejectedValue(err);
- return expect(savedObjectsClient.get(doc.type, doc.id)).rejects.toMatchInlineSnapshot(`
- Object {
- "body": "response body",
- "res": Object {
- "ok": false,
- "redirected": false,
- "status": 409,
- "statusText": "Conflict",
- },
- }
- `);
- });
});
diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts
index dab98ee66cdb1..ccb23793a8534 100644
--- a/src/core/public/saved_objects/saved_objects_client.ts
+++ b/src/core/public/saved_objects/saved_objects_client.ts
@@ -465,11 +465,7 @@ export class SavedObjectsClient {
* uses `{response: {status: number}}`.
*/
private savedObjectsFetch(path: string, { method, query, body }: HttpFetchOptions) {
- return this.http.fetch(path, { method, query, body }).catch(err => {
- const kfetchError = Object.assign(err, { res: err.response });
- delete kfetchError.response;
- return Promise.reject(kfetchError);
- });
+ return this.http.fetch(path, { method, query, body });
}
}
diff --git a/src/plugins/management/public/index.ts b/src/plugins/management/public/index.ts
index faec466dbd671..4ece75bbf36da 100644
--- a/src/plugins/management/public/index.ts
+++ b/src/plugins/management/public/index.ts
@@ -24,7 +24,12 @@ export function plugin(initializerContext: PluginInitializerContext) {
return new ManagementPlugin();
}
-export { ManagementSetup, ManagementStart, RegisterManagementApp } from './types';
+export {
+ ManagementSetup,
+ ManagementStart,
+ RegisterManagementApp,
+ RegisterManagementAppArgs,
+} from './types';
export { ManagementApp } from './management_app';
export { ManagementSection } from './management_section';
export { ManagementSidebarNav } from './components'; // for use in legacy management apps
diff --git a/x-pack/legacy/plugins/security/common/model.ts b/x-pack/legacy/plugins/security/common/model.ts
deleted file mode 100644
index 733e89f774db8..0000000000000
--- a/x-pack/legacy/plugins/security/common/model.ts
+++ /dev/null
@@ -1,28 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export {
- ApiKey,
- ApiKeyToInvalidate,
- AuthenticatedUser,
- BuiltinESPrivileges,
- EditUser,
- FeaturesPrivileges,
- InlineRoleTemplate,
- InvalidRoleTemplate,
- KibanaPrivileges,
- RawKibanaFeaturePrivileges,
- RawKibanaPrivileges,
- Role,
- RoleIndexPrivilege,
- RoleKibanaPrivilege,
- RoleMapping,
- RoleTemplate,
- StoredRoleTemplate,
- User,
- canUserChangePassword,
- getUserDisplayName,
-} from '../../../../plugins/security/common/model';
diff --git a/x-pack/legacy/plugins/security/index.d.ts b/x-pack/legacy/plugins/security/index.d.ts
index 18284c8be689a..d453415f73376 100644
--- a/x-pack/legacy/plugins/security/index.d.ts
+++ b/x-pack/legacy/plugins/security/index.d.ts
@@ -5,7 +5,7 @@
*/
import { Legacy } from 'kibana';
-import { AuthenticatedUser } from './common/model';
+import { AuthenticatedUser } from '../../../plugins/security/public';
/**
* Public interface of the security plugin.
diff --git a/x-pack/legacy/plugins/security/index.js b/x-pack/legacy/plugins/security/index.js
index bc403b803b8df..4988c30a1398b 100644
--- a/x-pack/legacy/plugins/security/index.js
+++ b/x-pack/legacy/plugins/security/index.js
@@ -40,8 +40,6 @@ export const security = kibana =>
},
uiExports: {
- chromeNavControls: [],
- managementSections: ['plugins/security/views/management'],
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
apps: [
{
@@ -76,7 +74,6 @@ export const security = kibana =>
'plugins/security/hacks/on_unauthorized_response',
'plugins/security/hacks/register_account_management_app',
],
- home: ['plugins/security/register_feature'],
injectDefaultVars: server => {
const securityPlugin = server.newPlatform.setup.plugins.security;
if (!securityPlugin) {
diff --git a/x-pack/legacy/plugins/security/public/documentation_links.js b/x-pack/legacy/plugins/security/public/documentation_links.js
deleted file mode 100644
index 8050289b95e9d..0000000000000
--- a/x-pack/legacy/plugins/security/public/documentation_links.js
+++ /dev/null
@@ -1,16 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links';
-
-const ES_REF_URL = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}`;
-
-export const documentationLinks = {
- dashboardViewMode: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/xpack-view-modes.html`,
- esClusterPrivileges: `${ES_REF_URL}/security-privileges.html#privileges-list-cluster`,
- esIndicesPrivileges: `${ES_REF_URL}/security-privileges.html#privileges-list-indices`,
- esRunAsPrivileges: `${ES_REF_URL}/security-privileges.html#_run_as_privilege`,
-};
diff --git a/x-pack/legacy/plugins/security/public/images/logout.svg b/x-pack/legacy/plugins/security/public/images/logout.svg
deleted file mode 100644
index d6533c0719904..0000000000000
--- a/x-pack/legacy/plugins/security/public/images/logout.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
\ No newline at end of file
diff --git a/x-pack/legacy/plugins/security/public/images/person.svg b/x-pack/legacy/plugins/security/public/images/person.svg
deleted file mode 100644
index 988ddac8859d7..0000000000000
--- a/x-pack/legacy/plugins/security/public/images/person.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
\ No newline at end of file
diff --git a/x-pack/legacy/plugins/security/public/index.scss b/x-pack/legacy/plugins/security/public/index.scss
index 2d7696bed3989..187ad5231534d 100644
--- a/x-pack/legacy/plugins/security/public/index.scss
+++ b/x-pack/legacy/plugins/security/public/index.scss
@@ -15,3 +15,6 @@ $secFormWidth: 460px;
// Public views
@import './views/index';
+// Styles of Kibana Platform plugin
+@import '../../../../plugins/security/public/index';
+
diff --git a/x-pack/legacy/plugins/security/public/lib/__tests__/util.js b/x-pack/legacy/plugins/security/public/lib/__tests__/util.js
deleted file mode 100644
index 3f7d8aea53a85..0000000000000
--- a/x-pack/legacy/plugins/security/public/lib/__tests__/util.js
+++ /dev/null
@@ -1,49 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import expect from '@kbn/expect';
-import { toggle, toggleSort } from '../../../public/lib/util';
-
-describe('util', () => {
- describe('toggle', () => {
- it('should add an item to a collection if not already included', () => {
- const collection = [1, 2, 3, 4, 5];
- toggle(collection, 6);
- expect(collection.indexOf(6)).to.be.above(0);
- });
-
- it('should remove an item from a collection if already included', () => {
- const collection = [1, 2, 3, 4, 5];
- toggle(collection, 3);
- expect(collection.indexOf(3)).to.be.below(0);
- });
- });
-
- describe('toggleSort', () => {
- it('should toggle reverse if called with the same orderBy', () => {
- const sort = { orderBy: 'foo', reverse: false };
-
- toggleSort(sort, 'foo');
- expect(sort.reverse).to.be.true;
-
- toggleSort(sort, 'foo');
- expect(sort.reverse).to.be.false;
- });
-
- it('should change orderBy and set reverse to false when called with a different orderBy', () => {
- const sort = { orderBy: 'foo', reverse: false };
-
- toggleSort(sort, 'bar');
- expect(sort.orderBy).to.equal('bar');
- expect(sort.reverse).to.be.false;
-
- sort.reverse = true;
- toggleSort(sort, 'foo');
- expect(sort.orderBy).to.equal('foo');
- expect(sort.reverse).to.be.false;
- });
- });
-});
diff --git a/x-pack/legacy/plugins/security/public/lib/api.ts b/x-pack/legacy/plugins/security/public/lib/api.ts
deleted file mode 100644
index c5c6994bf4be3..0000000000000
--- a/x-pack/legacy/plugins/security/public/lib/api.ts
+++ /dev/null
@@ -1,56 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { kfetch } from 'ui/kfetch';
-import { Role, User, EditUser } from '../../common/model';
-
-const usersUrl = '/internal/security/users';
-const rolesUrl = '/api/security/role';
-
-export class UserAPIClient {
- public async getUsers(): Promise {
- return await kfetch({ pathname: usersUrl });
- }
-
- public async getUser(username: string): Promise {
- const url = `${usersUrl}/${encodeURIComponent(username)}`;
- return await kfetch({ pathname: url });
- }
-
- public async deleteUser(username: string) {
- const url = `${usersUrl}/${encodeURIComponent(username)}`;
- await kfetch({ pathname: url, method: 'DELETE' }, {});
- }
-
- public async saveUser(user: EditUser) {
- const url = `${usersUrl}/${encodeURIComponent(user.username)}`;
-
- await kfetch({ pathname: url, body: JSON.stringify(user), method: 'POST' });
- }
-
- public async getRoles(): Promise {
- return await kfetch({ pathname: rolesUrl });
- }
-
- public async getRole(name: string): Promise {
- const url = `${rolesUrl}/${encodeURIComponent(name)}`;
- return await kfetch({ pathname: url });
- }
-
- public async changePassword(username: string, password: string, currentPassword: string) {
- const data: Record = {
- newPassword: password,
- };
- if (currentPassword) {
- data.password = currentPassword;
- }
- await kfetch({
- pathname: `${usersUrl}/${encodeURIComponent(username)}/password`,
- method: 'POST',
- body: JSON.stringify(data),
- });
- }
-}
diff --git a/x-pack/legacy/plugins/security/public/lib/api_keys_api.ts b/x-pack/legacy/plugins/security/public/lib/api_keys_api.ts
deleted file mode 100644
index fbc0460c5908a..0000000000000
--- a/x-pack/legacy/plugins/security/public/lib/api_keys_api.ts
+++ /dev/null
@@ -1,47 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { kfetch } from 'ui/kfetch';
-import { ApiKey, ApiKeyToInvalidate } from '../../common/model';
-
-interface CheckPrivilegesResponse {
- areApiKeysEnabled: boolean;
- isAdmin: boolean;
-}
-
-interface InvalidateApiKeysResponse {
- itemsInvalidated: ApiKeyToInvalidate[];
- errors: any[];
-}
-
-interface GetApiKeysResponse {
- apiKeys: ApiKey[];
-}
-
-const apiKeysUrl = `/internal/security/api_key`;
-
-export class ApiKeysApi {
- public static async checkPrivileges(): Promise {
- return kfetch({ pathname: `${apiKeysUrl}/privileges` });
- }
-
- public static async getApiKeys(isAdmin: boolean = false): Promise {
- const query = {
- isAdmin,
- };
-
- return kfetch({ pathname: apiKeysUrl, query });
- }
-
- public static async invalidateApiKeys(
- apiKeys: ApiKeyToInvalidate[],
- isAdmin: boolean = false
- ): Promise {
- const pathname = `${apiKeysUrl}/invalidate`;
- const body = JSON.stringify({ apiKeys, isAdmin });
- return kfetch({ pathname, method: 'POST', body });
- }
-}
diff --git a/x-pack/legacy/plugins/security/public/lib/role_utils.ts b/x-pack/legacy/plugins/security/public/lib/role_utils.ts
deleted file mode 100644
index c33b7385306fb..0000000000000
--- a/x-pack/legacy/plugins/security/public/lib/role_utils.ts
+++ /dev/null
@@ -1,58 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { cloneDeep, get } from 'lodash';
-import { Role } from '../../common/model';
-
-/**
- * Returns whether given role is enabled or not
- *
- * @param role Object Role JSON, as returned by roles API
- * @return Boolean true if role is enabled; false otherwise
- */
-export function isRoleEnabled(role: Partial) {
- return get(role, 'transient_metadata.enabled', true);
-}
-
-/**
- * Returns whether given role is reserved or not.
- *
- * @param {role} the Role as returned by roles API
- */
-export function isReservedRole(role: Partial) {
- return get(role, 'metadata._reserved', false);
-}
-
-/**
- * Returns whether given role is editable through the UI or not.
- *
- * @param role the Role as returned by roles API
- */
-export function isReadOnlyRole(role: Partial): boolean {
- return isReservedRole(role) || !!(role._transform_error && role._transform_error.length > 0);
-}
-
-/**
- * Returns a deep copy of the role.
- *
- * @param role the Role to copy.
- */
-export function copyRole(role: Role) {
- return cloneDeep(role);
-}
-
-/**
- * Creates a deep copy of the role suitable for cloning.
- *
- * @param role the Role to clone.
- */
-export function prepareRoleClone(role: Role): Role {
- const clone = copyRole(role);
-
- clone.name = '';
-
- return clone;
-}
diff --git a/x-pack/legacy/plugins/security/public/lib/roles_api.ts b/x-pack/legacy/plugins/security/public/lib/roles_api.ts
deleted file mode 100644
index 20c1491ccaac6..0000000000000
--- a/x-pack/legacy/plugins/security/public/lib/roles_api.ts
+++ /dev/null
@@ -1,25 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { kfetch } from 'ui/kfetch';
-import { Role } from '../../common/model';
-
-export class RolesApi {
- public static async getRoles(): Promise {
- return kfetch({ pathname: '/api/security/role' });
- }
-
- public static async getRole(roleName: string): Promise {
- return kfetch({ pathname: `/api/security/role/${encodeURIComponent(roleName)}` });
- }
-
- public static async deleteRole(roleName: string) {
- return kfetch({
- pathname: `/api/security/role/${encodeURIComponent(roleName)}`,
- method: 'DELETE',
- });
- }
-}
diff --git a/x-pack/legacy/plugins/security/public/lib/transform_role_for_save.ts b/x-pack/legacy/plugins/security/public/lib/transform_role_for_save.ts
deleted file mode 100644
index 861ba530050a1..0000000000000
--- a/x-pack/legacy/plugins/security/public/lib/transform_role_for_save.ts
+++ /dev/null
@@ -1,41 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { Role, RoleIndexPrivilege } from '../../common/model';
-import { isGlobalPrivilegeDefinition } from './privilege_utils';
-
-export function transformRoleForSave(role: Role, spacesEnabled: boolean) {
- // Remove any placeholder index privileges
- role.elasticsearch.indices = role.elasticsearch.indices.filter(
- indexPrivilege => !isPlaceholderPrivilege(indexPrivilege)
- );
-
- // Remove any placeholder query entries
- role.elasticsearch.indices.forEach(index => index.query || delete index.query);
-
- // If spaces are disabled, then do not persist any space privileges
- if (!spacesEnabled) {
- role.kibana = role.kibana.filter(isGlobalPrivilegeDefinition);
- }
-
- role.kibana.forEach(kibanaPrivilege => {
- // If a base privilege is defined, then do not persist feature privileges
- if (kibanaPrivilege.base.length > 0) {
- kibanaPrivilege.feature = {};
- }
- });
-
- delete role.name;
- delete role.transient_metadata;
- delete role._unrecognized_applications;
- delete role._transform_error;
-
- return role;
-}
-
-function isPlaceholderPrivilege(indexPrivilege: RoleIndexPrivilege) {
- return indexPrivilege.names.length === 0;
-}
diff --git a/x-pack/legacy/plugins/security/public/lib/util.js b/x-pack/legacy/plugins/security/public/lib/util.js
deleted file mode 100644
index bdf44aa3f10bb..0000000000000
--- a/x-pack/legacy/plugins/security/public/lib/util.js
+++ /dev/null
@@ -1,19 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export function toggle(collection, item) {
- const i = collection.indexOf(item);
- if (i >= 0) collection.splice(i, 1);
- else collection.push(item);
-}
-
-export function toggleSort(sort, orderBy) {
- if (sort.orderBy === orderBy) sort.reverse = !sort.reverse;
- else {
- sort.orderBy = orderBy;
- sort.reverse = false;
- }
-}
diff --git a/x-pack/legacy/plugins/security/public/objects/lib/get_fields.ts b/x-pack/legacy/plugins/security/public/objects/lib/get_fields.ts
deleted file mode 100644
index 91d98782dab42..0000000000000
--- a/x-pack/legacy/plugins/security/public/objects/lib/get_fields.ts
+++ /dev/null
@@ -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;
- * you may not use this file except in compliance with the Elastic License.
- */
-import { IHttpResponse } from 'angular';
-import chrome from 'ui/chrome';
-
-const apiBase = chrome.addBasePath(`/internal/security/fields`);
-
-export async function getFields($http: any, query: string): Promise {
- return await $http
- .get(`${apiBase}/${query}`)
- .then((response: IHttpResponse) => response.data || []);
-}
diff --git a/x-pack/legacy/plugins/security/public/objects/lib/roles.ts b/x-pack/legacy/plugins/security/public/objects/lib/roles.ts
deleted file mode 100644
index e33cbe4c6c031..0000000000000
--- a/x-pack/legacy/plugins/security/public/objects/lib/roles.ts
+++ /dev/null
@@ -1,21 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-import chrome from 'ui/chrome';
-import { Role } from '../../../common/model';
-import { copyRole } from '../../lib/role_utils';
-import { transformRoleForSave } from '../../lib/transform_role_for_save';
-
-const apiBase = chrome.addBasePath(`/api/security/role`);
-
-export async function saveRole($http: any, role: Role, spacesEnabled: boolean) {
- const data = transformRoleForSave(copyRole(role), spacesEnabled);
-
- return await $http.put(`${apiBase}/${role.name}`, data);
-}
-
-export async function deleteRole($http: any, name: string) {
- return await $http.delete(`${apiBase}/${name}`);
-}
diff --git a/x-pack/legacy/plugins/security/public/register_feature.js b/x-pack/legacy/plugins/security/public/register_feature.js
deleted file mode 100644
index c0bd42690b6fd..0000000000000
--- a/x-pack/legacy/plugins/security/public/register_feature.js
+++ /dev/null
@@ -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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import {
- FeatureCatalogueRegistryProvider,
- FeatureCatalogueCategory,
-} from 'ui/registry/feature_catalogue';
-
-import { i18n } from '@kbn/i18n';
-
-FeatureCatalogueRegistryProvider.register(() => {
- return {
- id: 'security',
- title: i18n.translate('xpack.security.registerFeature.securitySettingsTitle', {
- defaultMessage: 'Security Settings',
- }),
- description: i18n.translate('xpack.security.registerFeature.securitySettingsDescription', {
- defaultMessage:
- 'Protect your data and easily manage who has access to what with users and roles.',
- }),
- icon: 'securityApp',
- path: '/app/kibana#/management/security',
- showOnHomePage: true,
- category: FeatureCatalogueCategory.ADMIN,
- };
-});
diff --git a/x-pack/legacy/plugins/security/public/services/shield_indices.js b/x-pack/legacy/plugins/security/public/services/shield_indices.js
deleted file mode 100644
index 791fa6cb59648..0000000000000
--- a/x-pack/legacy/plugins/security/public/services/shield_indices.js
+++ /dev/null
@@ -1,18 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { uiModules } from 'ui/modules';
-
-const module = uiModules.get('security', []);
-module.service('shieldIndices', ($http, chrome) => {
- return {
- getFields: query => {
- return $http
- .get(chrome.addBasePath(`/internal/security/fields/${query}`))
- .then(response => response.data);
- },
- };
-});
diff --git a/x-pack/legacy/plugins/security/public/services/shield_role.js b/x-pack/legacy/plugins/security/public/services/shield_role.js
deleted file mode 100644
index 261d3449a7a2d..0000000000000
--- a/x-pack/legacy/plugins/security/public/services/shield_role.js
+++ /dev/null
@@ -1,30 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import 'angular-resource';
-import { omit } from 'lodash';
-import angular from 'angular';
-import { uiModules } from 'ui/modules';
-
-const module = uiModules.get('security', ['ngResource']);
-module.service('ShieldRole', ($resource, chrome) => {
- return $resource(
- chrome.addBasePath('/api/security/role/:name'),
- {
- name: '@name',
- },
- {
- save: {
- method: 'PUT',
- transformRequest(data) {
- return angular.toJson(
- omit(data, 'name', 'transient_metadata', '_unrecognized_applications')
- );
- },
- },
- }
- );
-});
diff --git a/x-pack/legacy/plugins/security/public/views/_index.scss b/x-pack/legacy/plugins/security/public/views/_index.scss
index b85a7e1997390..6c2a091adf536 100644
--- a/x-pack/legacy/plugins/security/public/views/_index.scss
+++ b/x-pack/legacy/plugins/security/public/views/_index.scss
@@ -1,5 +1,2 @@
// Login styles
@import './login/index';
-
-// Management styles
-@import './management/index';
diff --git a/x-pack/legacy/plugins/security/public/views/account/account.html b/x-pack/legacy/plugins/security/public/views/account/account.html
deleted file mode 100644
index 0935c415b1829..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/account/account.html
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/x-pack/legacy/plugins/security/public/views/account/account.js b/x-pack/legacy/plugins/security/public/views/account/account.js
index 70a7b8dce727e..13abc44e08f96 100644
--- a/x-pack/legacy/plugins/security/public/views/account/account.js
+++ b/x-pack/legacy/plugins/security/public/views/account/account.js
@@ -4,17 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import routes from 'ui/routes';
-import template from './account.html';
-import { i18n } from '@kbn/i18n';
-import { I18nContext } from 'ui/i18n';
-import { npSetup } from 'ui/new_platform';
-import { AccountManagementPage } from './components';
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
+import { i18n } from '@kbn/i18n';
+import { npStart } from 'ui/new_platform';
+import routes from 'ui/routes';
routes.when('/account', {
- template,
+ template: '',
k7Breadcrumbs: () => [
{
text: i18n.translate('xpack.security.account.breadcrumb', {
@@ -24,19 +21,15 @@ routes.when('/account', {
],
controllerAs: 'accountController',
controller($scope) {
- $scope.$on('$destroy', () => {
- const elem = document.getElementById('userProfileReactRoot');
- if (elem) {
- unmountComponentAtNode(elem);
- }
- });
$scope.$$postDigest(() => {
+ const domNode = document.getElementById('userProfileReactRoot');
+
render(
-
-
- ,
- document.getElementById('userProfileReactRoot')
+ ,
+ domNode
);
+
+ $scope.$on('$destroy', () => unmountComponentAtNode(domNode));
});
},
});
diff --git a/x-pack/legacy/plugins/security/public/views/login/_index.scss b/x-pack/legacy/plugins/security/public/views/login/_index.scss
index 9f133940f7977..9083c8dc3b775 100644
--- a/x-pack/legacy/plugins/security/public/views/login/_index.scss
+++ b/x-pack/legacy/plugins/security/public/views/login/_index.scss
@@ -5,5 +5,4 @@
// loginChart__legend--small
// loginChart__legend-isLoading
-@import 'login';
-
+@import './components/index';
diff --git a/x-pack/legacy/plugins/security/public/views/login/components/_index.scss b/x-pack/legacy/plugins/security/public/views/login/components/_index.scss
new file mode 100644
index 0000000000000..a6f9598b9cc04
--- /dev/null
+++ b/x-pack/legacy/plugins/security/public/views/login/components/_index.scss
@@ -0,0 +1 @@
+@import './login_page/index';
diff --git a/x-pack/legacy/plugins/security/public/views/login/components/basic_login_form/basic_login_form.test.tsx b/x-pack/legacy/plugins/security/public/views/login/components/basic_login_form/basic_login_form.test.tsx
index 93451453a523a..3a970d582bdc8 100644
--- a/x-pack/legacy/plugins/security/public/views/login/components/basic_login_form/basic_login_form.test.tsx
+++ b/x-pack/legacy/plugins/security/public/views/login/components/basic_login_form/basic_login_form.test.tsx
@@ -7,7 +7,7 @@
import { EuiButton, EuiCallOut } from '@elastic/eui';
import React from 'react';
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
-import { LoginState } from '../../../../../common/login_state';
+import { LoginState } from '../../login_state';
import { BasicLoginForm } from './basic_login_form';
const createMockHttp = ({ simulateError = false } = {}) => {
diff --git a/x-pack/legacy/plugins/security/public/views/login/components/basic_login_form/basic_login_form.tsx b/x-pack/legacy/plugins/security/public/views/login/components/basic_login_form/basic_login_form.tsx
index e6d3b5b7536b6..c263381fbdb56 100644
--- a/x-pack/legacy/plugins/security/public/views/login/components/basic_login_form/basic_login_form.tsx
+++ b/x-pack/legacy/plugins/security/public/views/login/components/basic_login_form/basic_login_form.tsx
@@ -9,7 +9,7 @@ import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
import React, { ChangeEvent, Component, FormEvent, Fragment, MouseEvent } from 'react';
import ReactMarkdown from 'react-markdown';
import { EuiText } from '@elastic/eui';
-import { LoginState } from '../../../../../common/login_state';
+import { LoginState } from '../../login_state';
interface Props {
http: any;
diff --git a/x-pack/legacy/plugins/security/public/views/login/components/login_page/_index.scss b/x-pack/legacy/plugins/security/public/views/login/components/login_page/_index.scss
new file mode 100644
index 0000000000000..4dd2c0cabfb5e
--- /dev/null
+++ b/x-pack/legacy/plugins/security/public/views/login/components/login_page/_index.scss
@@ -0,0 +1 @@
+@import './login_page';
diff --git a/x-pack/legacy/plugins/security/public/views/login/_login.scss b/x-pack/legacy/plugins/security/public/views/login/components/login_page/_login_page.scss
similarity index 88%
rename from x-pack/legacy/plugins/security/public/views/login/_login.scss
rename to x-pack/legacy/plugins/security/public/views/login/components/login_page/_login_page.scss
index 607e9e6ec5e3f..cdfad55ee064a 100644
--- a/x-pack/legacy/plugins/security/public/views/login/_login.scss
+++ b/x-pack/legacy/plugins/security/public/views/login/components/login_page/_login_page.scss
@@ -1,4 +1,3 @@
-
.loginWelcome {
@include kibanaFullScreenGraphics;
}
@@ -16,10 +15,6 @@
margin-bottom: $euiSizeXL;
}
-.loginWelcome__footerAction {
- margin-right: $euiSizeS;
-}
-
.loginWelcome__content {
position: relative;
margin: auto;
diff --git a/x-pack/legacy/plugins/security/public/views/login/components/login_page/login_page.test.tsx b/x-pack/legacy/plugins/security/public/views/login/components/login_page/login_page.test.tsx
index c16db007bdbdc..a0318d50a45e5 100644
--- a/x-pack/legacy/plugins/security/public/views/login/components/login_page/login_page.test.tsx
+++ b/x-pack/legacy/plugins/security/public/views/login/components/login_page/login_page.test.tsx
@@ -6,7 +6,7 @@
import { shallow } from 'enzyme';
import React from 'react';
-import { LoginLayout, LoginState } from '../../../../../common/login_state';
+import { LoginLayout, LoginState } from '../../login_state';
import { LoginPage } from './login_page';
const createMockHttp = ({ simulateError = false } = {}) => {
diff --git a/x-pack/legacy/plugins/security/public/views/login/components/login_page/login_page.tsx b/x-pack/legacy/plugins/security/public/views/login/components/login_page/login_page.tsx
index e7e56947ca58f..8035789a30e9d 100644
--- a/x-pack/legacy/plugins/security/public/views/login/components/login_page/login_page.tsx
+++ b/x-pack/legacy/plugins/security/public/views/login/components/login_page/login_page.tsx
@@ -19,7 +19,7 @@ import {
EuiTitle,
} from '@elastic/eui';
import classNames from 'classnames';
-import { LoginState } from '../../../../../common/login_state';
+import { LoginState } from '../../login_state';
import { BasicLoginForm } from '../basic_login_form';
import { DisabledLoginForm } from '../disabled_login_form';
diff --git a/x-pack/legacy/plugins/security/public/views/login/login.html b/x-pack/legacy/plugins/security/public/views/login/login.html
deleted file mode 100644
index 2695fabdd6367..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/login/login.html
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/x-pack/legacy/plugins/security/public/views/login/login.tsx b/x-pack/legacy/plugins/security/public/views/login/login.tsx
index d9daf2d1f4d0d..0b89ac553c9a8 100644
--- a/x-pack/legacy/plugins/security/public/views/login/login.tsx
+++ b/x-pack/legacy/plugins/security/public/views/login/login.tsx
@@ -6,16 +6,14 @@
import { i18n } from '@kbn/i18n';
import { get } from 'lodash';
-import { parseNext } from 'plugins/security/lib/parse_next';
import { LoginPage } from 'plugins/security/views/login/components';
-// @ts-ignore
-import template from 'plugins/security/views/login/login.html';
import React from 'react';
import { render } from 'react-dom';
import chrome from 'ui/chrome';
import { I18nContext } from 'ui/i18n';
import { parse } from 'url';
-import { LoginState } from '../../../common/login_state';
+import { parseNext } from './parse_next';
+import { LoginState } from './login_state';
const messageMap = {
SESSION_EXPIRED: i18n.translate('xpack.security.login.sessionExpiredDescription', {
defaultMessage: 'Your session has timed out. Please log in again.',
@@ -31,7 +29,7 @@ interface AnyObject {
(chrome as AnyObject)
.setVisible(false)
- .setRootTemplate(template)
+ .setRootTemplate('')
.setRootController(
'login',
(
diff --git a/x-pack/legacy/plugins/security/common/login_state.ts b/x-pack/legacy/plugins/security/public/views/login/login_state.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/common/login_state.ts
rename to x-pack/legacy/plugins/security/public/views/login/login_state.ts
diff --git a/x-pack/legacy/plugins/security/public/lib/__tests__/parse_next.js b/x-pack/legacy/plugins/security/public/views/login/parse_next.test.ts
similarity index 80%
rename from x-pack/legacy/plugins/security/public/lib/__tests__/parse_next.js
rename to x-pack/legacy/plugins/security/public/views/login/parse_next.test.ts
index 7516433c77f83..b5e6c7dca41d8 100644
--- a/x-pack/legacy/plugins/security/public/lib/__tests__/parse_next.js
+++ b/x-pack/legacy/plugins/security/public/views/login/parse_next.test.ts
@@ -4,12 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import expect from '@kbn/expect';
-import { parseNext } from '../parse_next';
+import { parseNext } from './parse_next';
describe('parseNext', () => {
it('should return a function', () => {
- expect(parseNext).to.be.a('function');
+ expect(parseNext).toBeInstanceOf(Function);
});
describe('with basePath defined', () => {
@@ -17,14 +16,14 @@ describe('parseNext', () => {
it('should return basePath with a trailing slash when next is not specified', () => {
const basePath = '/iqf';
const href = `${basePath}/login`;
- expect(parseNext(href, basePath)).to.equal(`${basePath}/`);
+ expect(parseNext(href, basePath)).toEqual(`${basePath}/`);
});
it('should properly handle next without hash', () => {
const basePath = '/iqf';
const next = `${basePath}/app/kibana`;
const href = `${basePath}/login?next=${next}`;
- expect(parseNext(href, basePath)).to.equal(next);
+ expect(parseNext(href, basePath)).toEqual(next);
});
it('should properly handle next with hash', () => {
@@ -32,7 +31,7 @@ describe('parseNext', () => {
const next = `${basePath}/app/kibana`;
const hash = '/discover/New-Saved-Search';
const href = `${basePath}/login?next=${next}#${hash}`;
- expect(parseNext(href, basePath)).to.equal(`${next}#${hash}`);
+ expect(parseNext(href, basePath)).toEqual(`${next}#${hash}`);
});
it('should properly decode special characters', () => {
@@ -40,7 +39,7 @@ describe('parseNext', () => {
const next = `${encodeURIComponent(basePath)}%2Fapp%2Fkibana`;
const hash = '/discover/New-Saved-Search';
const href = `${basePath}/login?next=${next}#${hash}`;
- expect(parseNext(href, basePath)).to.equal(decodeURIComponent(`${next}#${hash}`));
+ expect(parseNext(href, basePath)).toEqual(decodeURIComponent(`${next}#${hash}`));
});
// to help prevent open redirect to a different url
@@ -48,7 +47,7 @@ describe('parseNext', () => {
const basePath = '/iqf';
const next = `https://example.com${basePath}/app/kibana`;
const href = `${basePath}/login?next=${next}`;
- expect(parseNext(href, basePath)).to.equal(`${basePath}/`);
+ expect(parseNext(href, basePath)).toEqual(`${basePath}/`);
});
// to help prevent open redirect to a different url by abusing encodings
@@ -58,7 +57,7 @@ describe('parseNext', () => {
const next = `${encodeURIComponent(baseUrl)}%2Fapp%2Fkibana`;
const hash = '/discover/New-Saved-Search';
const href = `${basePath}/login?next=${next}#${hash}`;
- expect(parseNext(href, basePath)).to.equal(`${basePath}/`);
+ expect(parseNext(href, basePath)).toEqual(`${basePath}/`);
});
// to help prevent open redirect to a different port
@@ -66,7 +65,7 @@ describe('parseNext', () => {
const basePath = '/iqf';
const next = `http://localhost:5601${basePath}/app/kibana`;
const href = `${basePath}/login?next=${next}`;
- expect(parseNext(href, basePath)).to.equal(`${basePath}/`);
+ expect(parseNext(href, basePath)).toEqual(`${basePath}/`);
});
// to help prevent open redirect to a different port by abusing encodings
@@ -76,7 +75,7 @@ describe('parseNext', () => {
const next = `${encodeURIComponent(baseUrl)}%2Fapp%2Fkibana`;
const hash = '/discover/New-Saved-Search';
const href = `${basePath}/login?next=${next}#${hash}`;
- expect(parseNext(href, basePath)).to.equal(`${basePath}/`);
+ expect(parseNext(href, basePath)).toEqual(`${basePath}/`);
});
// to help prevent open redirect to a different base path
@@ -84,18 +83,18 @@ describe('parseNext', () => {
const basePath = '/iqf';
const next = '/notbasepath/app/kibana';
const href = `${basePath}/login?next=${next}`;
- expect(parseNext(href, basePath)).to.equal(`${basePath}/`);
+ expect(parseNext(href, basePath)).toEqual(`${basePath}/`);
});
// disallow network-path references
it('should return / if next is url without protocol', () => {
const nextWithTwoSlashes = '//example.com';
const hrefWithTwoSlashes = `/login?next=${nextWithTwoSlashes}`;
- expect(parseNext(hrefWithTwoSlashes)).to.equal('/');
+ expect(parseNext(hrefWithTwoSlashes)).toEqual('/');
const nextWithThreeSlashes = '///example.com';
const hrefWithThreeSlashes = `/login?next=${nextWithThreeSlashes}`;
- expect(parseNext(hrefWithThreeSlashes)).to.equal('/');
+ expect(parseNext(hrefWithThreeSlashes)).toEqual('/');
});
});
@@ -103,34 +102,34 @@ describe('parseNext', () => {
// trailing slash is important since it must match the cookie path exactly
it('should return / with a trailing slash when next is not specified', () => {
const href = '/login';
- expect(parseNext(href)).to.equal('/');
+ expect(parseNext(href)).toEqual('/');
});
it('should properly handle next without hash', () => {
const next = '/app/kibana';
const href = `/login?next=${next}`;
- expect(parseNext(href)).to.equal(next);
+ expect(parseNext(href)).toEqual(next);
});
it('should properly handle next with hash', () => {
const next = '/app/kibana';
const hash = '/discover/New-Saved-Search';
const href = `/login?next=${next}#${hash}`;
- expect(parseNext(href)).to.equal(`${next}#${hash}`);
+ expect(parseNext(href)).toEqual(`${next}#${hash}`);
});
it('should properly decode special characters', () => {
const next = '%2Fapp%2Fkibana';
const hash = '/discover/New-Saved-Search';
const href = `/login?next=${next}#${hash}`;
- expect(parseNext(href)).to.equal(decodeURIComponent(`${next}#${hash}`));
+ expect(parseNext(href)).toEqual(decodeURIComponent(`${next}#${hash}`));
});
// to help prevent open redirect to a different url
it('should return / if next includes a protocol/hostname', () => {
const next = 'https://example.com/app/kibana';
const href = `/login?next=${next}`;
- expect(parseNext(href)).to.equal('/');
+ expect(parseNext(href)).toEqual('/');
});
// to help prevent open redirect to a different url by abusing encodings
@@ -139,14 +138,14 @@ describe('parseNext', () => {
const next = `${encodeURIComponent(baseUrl)}%2Fapp%2Fkibana`;
const hash = '/discover/New-Saved-Search';
const href = `/login?next=${next}#${hash}`;
- expect(parseNext(href)).to.equal('/');
+ expect(parseNext(href)).toEqual('/');
});
// to help prevent open redirect to a different port
it('should return / if next includes a port', () => {
const next = 'http://localhost:5601/app/kibana';
const href = `/login?next=${next}`;
- expect(parseNext(href)).to.equal('/');
+ expect(parseNext(href)).toEqual('/');
});
// to help prevent open redirect to a different port by abusing encodings
@@ -155,18 +154,18 @@ describe('parseNext', () => {
const next = `${encodeURIComponent(baseUrl)}%2Fapp%2Fkibana`;
const hash = '/discover/New-Saved-Search';
const href = `/login?next=${next}#${hash}`;
- expect(parseNext(href)).to.equal('/');
+ expect(parseNext(href)).toEqual('/');
});
// disallow network-path references
it('should return / if next is url without protocol', () => {
const nextWithTwoSlashes = '//example.com';
const hrefWithTwoSlashes = `/login?next=${nextWithTwoSlashes}`;
- expect(parseNext(hrefWithTwoSlashes)).to.equal('/');
+ expect(parseNext(hrefWithTwoSlashes)).toEqual('/');
const nextWithThreeSlashes = '///example.com';
const hrefWithThreeSlashes = `/login?next=${nextWithThreeSlashes}`;
- expect(parseNext(hrefWithThreeSlashes)).to.equal('/');
+ expect(parseNext(hrefWithThreeSlashes)).toEqual('/');
});
});
});
diff --git a/x-pack/legacy/plugins/security/public/lib/parse_next.ts b/x-pack/legacy/plugins/security/public/views/login/parse_next.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/lib/parse_next.ts
rename to x-pack/legacy/plugins/security/public/views/login/parse_next.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/_index.scss b/x-pack/legacy/plugins/security/public/views/management/_index.scss
deleted file mode 100644
index 78b53845071e4..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/_index.scss
+++ /dev/null
@@ -1,4 +0,0 @@
-@import './change_password_form/index';
-@import './edit_role/index';
-@import './edit_user/index';
-@import './role_mappings/edit_role_mapping/index';
\ No newline at end of file
diff --git a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/api_keys.html b/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/api_keys.html
deleted file mode 100644
index e46c6f72b5d20..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/api_keys.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/api_keys.js b/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/api_keys.js
deleted file mode 100644
index e7143b1020814..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/api_keys.js
+++ /dev/null
@@ -1,35 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-import React from 'react';
-import { render, unmountComponentAtNode } from 'react-dom';
-import routes from 'ui/routes';
-import template from './api_keys.html';
-import { API_KEYS_PATH } from '../management_urls';
-import { getApiKeysBreadcrumbs } from '../breadcrumbs';
-import { I18nContext } from 'ui/i18n';
-import { ApiKeysGridPage } from './components';
-
-routes.when(API_KEYS_PATH, {
- template,
- k7Breadcrumbs: getApiKeysBreadcrumbs,
- controller($scope) {
- $scope.$$postDigest(() => {
- const domNode = document.getElementById('apiKeysGridReactRoot');
-
- render(
-
-
- ,
- domNode
- );
-
- // unmount react on controller destroy
- $scope.$on('$destroy', () => {
- unmountComponentAtNode(domNode);
- });
- });
- },
-});
diff --git a/x-pack/legacy/plugins/security/public/views/management/breadcrumbs.ts b/x-pack/legacy/plugins/security/public/views/management/breadcrumbs.ts
deleted file mode 100644
index 4ab7e45e84849..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/breadcrumbs.ts
+++ /dev/null
@@ -1,115 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { i18n } from '@kbn/i18n';
-import { MANAGEMENT_BREADCRUMB } from 'ui/management/breadcrumbs';
-
-export function getUsersBreadcrumbs() {
- return [
- MANAGEMENT_BREADCRUMB,
- {
- text: i18n.translate('xpack.security.users.breadcrumb', {
- defaultMessage: 'Users',
- }),
- href: '#/management/security/users',
- },
- ];
-}
-
-export function getEditUserBreadcrumbs($route: Record) {
- const { username } = $route.current.params;
- return [
- ...getUsersBreadcrumbs(),
- {
- text: username,
- href: `#/management/security/users/edit/${username}`,
- },
- ];
-}
-
-export function getCreateUserBreadcrumbs() {
- return [
- ...getUsersBreadcrumbs(),
- {
- text: i18n.translate('xpack.security.users.createBreadcrumb', {
- defaultMessage: 'Create',
- }),
- },
- ];
-}
-
-export function getRolesBreadcrumbs() {
- return [
- MANAGEMENT_BREADCRUMB,
- {
- text: i18n.translate('xpack.security.roles.breadcrumb', {
- defaultMessage: 'Roles',
- }),
- href: '#/management/security/roles',
- },
- ];
-}
-
-export function getEditRoleBreadcrumbs($route: Record) {
- const { name } = $route.current.params;
- return [
- ...getRolesBreadcrumbs(),
- {
- text: name,
- href: `#/management/security/roles/edit/${name}`,
- },
- ];
-}
-
-export function getCreateRoleBreadcrumbs() {
- return [
- ...getUsersBreadcrumbs(),
- {
- text: i18n.translate('xpack.security.roles.createBreadcrumb', {
- defaultMessage: 'Create',
- }),
- },
- ];
-}
-
-export function getApiKeysBreadcrumbs() {
- return [
- MANAGEMENT_BREADCRUMB,
- {
- text: i18n.translate('xpack.security.apiKeys.breadcrumb', {
- defaultMessage: 'API Keys',
- }),
- href: '#/management/security/api_keys',
- },
- ];
-}
-
-export function getRoleMappingBreadcrumbs() {
- return [
- MANAGEMENT_BREADCRUMB,
- {
- text: i18n.translate('xpack.security.roleMapping.breadcrumb', {
- defaultMessage: 'Role Mappings',
- }),
- href: '#/management/security/role_mappings',
- },
- ];
-}
-
-export function getEditRoleMappingBreadcrumbs($route: Record) {
- const { name } = $route.current.params;
- return [
- ...getRoleMappingBreadcrumbs(),
- {
- text:
- name ||
- i18n.translate('xpack.security.roleMappings.createBreadcrumb', {
- defaultMessage: 'Create',
- }),
- href: `#/management/security/role_mappings/edit/${name}`,
- },
- ];
-}
diff --git a/x-pack/legacy/plugins/security/public/views/management/change_password_form/_change_password_form.scss b/x-pack/legacy/plugins/security/public/views/management/change_password_form/_change_password_form.scss
deleted file mode 100644
index 98331c2070a31..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/change_password_form/_change_password_form.scss
+++ /dev/null
@@ -1,17 +0,0 @@
-.secChangePasswordForm__panel {
- max-width: $secFormWidth;
-}
-
-.secChangePasswordForm__subLabel {
- margin-bottom: $euiSizeS;
-}
-
-.secChangePasswordForm__footer {
- display: flex;
- justify-content: flex-start;
- align-items: center;
-
- .kuiButton + .kuiButton {
- margin-left: $euiSizeS;
- }
-}
diff --git a/x-pack/legacy/plugins/security/public/views/management/change_password_form/_index.scss b/x-pack/legacy/plugins/security/public/views/management/change_password_form/_index.scss
deleted file mode 100644
index a6058b5ddebbf..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/change_password_form/_index.scss
+++ /dev/null
@@ -1 +0,0 @@
-@import './change_password_form';
diff --git a/x-pack/legacy/plugins/security/public/views/management/change_password_form/change_password_form.html b/x-pack/legacy/plugins/security/public/views/management/change_password_form/change_password_form.html
deleted file mode 100644
index 92fb95861a6f8..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/change_password_form/change_password_form.html
+++ /dev/null
@@ -1,141 +0,0 @@
-
-
-
-
diff --git a/x-pack/legacy/plugins/security/public/views/management/change_password_form/change_password_form.js b/x-pack/legacy/plugins/security/public/views/management/change_password_form/change_password_form.js
deleted file mode 100644
index d9aa59f6df142..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/change_password_form/change_password_form.js
+++ /dev/null
@@ -1,48 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { uiModules } from 'ui/modules';
-import template from './change_password_form.html';
-
-const module = uiModules.get('security', ['kibana']);
-module.directive('kbnChangePasswordForm', function() {
- return {
- template,
- scope: {
- requireCurrentPassword: '=',
- showKibanaWarning: '=',
- onChangePassword: '&',
- },
- restrict: 'E',
- replace: true,
- controllerAs: 'changePasswordController',
- controller: function($scope) {
- this.currentPassword = null;
- this.newPassword = null;
- this.newPasswordConfirmation = null;
- this.isFormVisible = false;
- this.isIncorrectPassword = false;
-
- this.showForm = () => {
- this.isFormVisible = true;
- };
-
- this.hideForm = () => {
- $scope.changePasswordForm.$setPristine();
- $scope.changePasswordForm.$setUntouched();
- this.currentPassword = null;
- this.newPassword = null;
- this.newPasswordConfirmation = null;
- this.isFormVisible = false;
- this.isIncorrectPassword = false;
- };
-
- this.onIncorrectPassword = () => {
- this.isIncorrectPassword = true;
- };
- },
- };
-});
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/_index.scss b/x-pack/legacy/plugins/security/public/views/management/edit_role/_index.scss
deleted file mode 100644
index 192091fb04e3c..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/_index.scss
+++ /dev/null
@@ -1 +0,0 @@
-@import './components/index';
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/_index.scss b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/_index.scss
deleted file mode 100644
index 32b3832e7a9fa..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/_index.scss
+++ /dev/null
@@ -1,9 +0,0 @@
-@import './collapsible_panel/collapsible_panel';
-@import './privileges/kibana/space_aware_privilege_section/index';
-@import './privileges/kibana/feature_table/index';
-@import './spaces_popover_list/spaces_popover_list';
-
-.secPrivilegeFeatureIcon {
- flex-shrink: 0;
- margin-right: $euiSizeS;
-}
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.test.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.test.tsx
deleted file mode 100644
index 67c32c8393171..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.test.tsx
+++ /dev/null
@@ -1,716 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { ReactWrapper } from 'enzyme';
-import React from 'react';
-import { mountWithIntl } from 'test_utils/enzyme_helpers';
-import { UICapabilities } from 'ui/capabilities';
-import { Space } from '../../../../../../spaces/common/model/space';
-import { Feature } from '../../../../../../../../plugins/features/public';
-// These modules should be moved into a common directory
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { Actions } from '../../../../../../../../plugins/security/server/authorization/actions';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { privilegesFactory } from '../../../../../../../../plugins/security/server/authorization/privileges';
-import { RawKibanaPrivileges, Role } from '../../../../../common/model';
-import { EditRolePage } from './edit_role_page';
-import { SimplePrivilegeSection } from './privileges/kibana/simple_privilege_section';
-import { SpaceAwarePrivilegeSection } from './privileges/kibana/space_aware_privilege_section';
-import { TransformErrorSection } from './privileges/kibana/transform_error_section';
-
-const buildFeatures = () => {
- return [
- {
- id: 'feature1',
- name: 'Feature 1',
- icon: 'addDataApp',
- app: ['feature1App'],
- privileges: {
- all: {
- app: ['feature1App'],
- ui: ['feature1-ui'],
- savedObject: {
- all: [],
- read: [],
- },
- },
- },
- },
- {
- id: 'feature2',
- name: 'Feature 2',
- icon: 'addDataApp',
- app: ['feature2App'],
- privileges: {
- all: {
- app: ['feature2App'],
- ui: ['feature2-ui'],
- savedObject: {
- all: ['feature2'],
- read: ['config'],
- },
- },
- },
- },
- ] as Feature[];
-};
-
-const buildRawKibanaPrivileges = () => {
- return privilegesFactory(new Actions('unit_test_version'), {
- getFeatures: () => buildFeatures(),
- }).get();
-};
-
-const buildBuiltinESPrivileges = () => {
- return {
- cluster: ['all', 'manage', 'monitor'],
- index: ['all', 'read', 'write', 'index'],
- };
-};
-
-const buildUICapabilities = (canManageSpaces = true) => {
- return {
- catalogue: {},
- management: {},
- navLinks: {},
- spaces: {
- manage: canManageSpaces,
- },
- } as UICapabilities;
-};
-
-const buildSpaces = () => {
- return [
- {
- id: 'default',
- name: 'Default',
- disabledFeatures: [],
- _reserved: true,
- },
- {
- id: 'space_1',
- name: 'Space 1',
- disabledFeatures: [],
- },
- {
- id: 'space_2',
- name: 'Space 2',
- disabledFeatures: ['feature2'],
- },
- ] as Space[];
-};
-
-const expectReadOnlyFormButtons = (wrapper: ReactWrapper) => {
- expect(wrapper.find('button[data-test-subj="roleFormReturnButton"]')).toHaveLength(1);
- expect(wrapper.find('button[data-test-subj="roleFormSaveButton"]')).toHaveLength(0);
-};
-
-const expectSaveFormButtons = (wrapper: ReactWrapper) => {
- expect(wrapper.find('button[data-test-subj="roleFormReturnButton"]')).toHaveLength(0);
- expect(wrapper.find('button[data-test-subj="roleFormSaveButton"]')).toHaveLength(1);
-};
-
-describe('', () => {
- describe('with spaces enabled', () => {
- it('can render a reserved role', () => {
- const role: Role = {
- name: 'superuser',
- metadata: {
- _reserved: true,
- },
- elasticsearch: {
- cluster: ['all'],
- indices: [],
- run_as: ['*'],
- },
- kibana: [
- {
- spaces: ['*'],
- base: ['all'],
- feature: {},
- },
- ],
- };
-
- const features: Feature[] = buildFeatures();
- const mockHttpClient = jest.fn();
- const indexPatterns: string[] = ['foo*', 'bar*'];
- const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges();
- const builtinESPrivileges = buildBuiltinESPrivileges();
- const spaces: Space[] = buildSpaces();
- const uiCapabilities: UICapabilities = buildUICapabilities();
-
- const wrapper = mountWithIntl(
-
- );
-
- expect(wrapper.find('[data-test-subj="reservedRoleBadgeTooltip"]')).toHaveLength(1);
- expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1);
- expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0);
- expectReadOnlyFormButtons(wrapper);
- });
-
- it('can render a user defined role', () => {
- const role: Role = {
- name: 'my custom role',
- metadata: {},
- elasticsearch: {
- cluster: ['all'],
- indices: [],
- run_as: ['*'],
- },
- kibana: [
- {
- spaces: ['*'],
- base: ['all'],
- feature: {},
- },
- ],
- };
-
- const features: Feature[] = buildFeatures();
- const mockHttpClient = jest.fn();
- const indexPatterns: string[] = ['foo*', 'bar*'];
- const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges();
- const builtinESPrivileges = buildBuiltinESPrivileges();
- const spaces: Space[] = buildSpaces();
- const uiCapabilities: UICapabilities = buildUICapabilities();
-
- const wrapper = mountWithIntl(
-
- );
-
- expect(wrapper.find('[data-test-subj="reservedRoleBadgeTooltip"]')).toHaveLength(0);
- expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1);
- expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0);
- expectSaveFormButtons(wrapper);
- });
-
- it('can render when creating a new role', () => {
- // @ts-ignore
- const role: Role = {
- metadata: {},
- elasticsearch: {
- cluster: [],
- indices: [],
- run_as: [],
- },
- kibana: [],
- };
-
- const features: Feature[] = buildFeatures();
- const mockHttpClient = jest.fn();
- const indexPatterns: string[] = ['foo*', 'bar*'];
- const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges();
- const builtinESPrivileges = buildBuiltinESPrivileges();
- const spaces: Space[] = buildSpaces();
- const uiCapabilities: UICapabilities = buildUICapabilities();
-
- const wrapper = mountWithIntl(
-
- );
-
- expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1);
- expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0);
- expectSaveFormButtons(wrapper);
- });
-
- it('can render when cloning an existing role', () => {
- const role: Role = {
- metadata: {
- _reserved: false,
- },
- name: '',
- elasticsearch: {
- cluster: ['all', 'manage'],
- indices: [
- {
- names: ['foo*'],
- privileges: ['all'],
- field_security: {
- except: ['f'],
- grant: ['b*'],
- },
- },
- ],
- run_as: ['elastic'],
- },
- kibana: [
- {
- spaces: ['*'],
- base: ['all'],
- feature: {},
- },
- ],
- };
-
- const features: Feature[] = buildFeatures();
- const mockHttpClient = jest.fn();
- const indexPatterns: string[] = ['foo*', 'bar*'];
- const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges();
- const builtinESPrivileges = buildBuiltinESPrivileges();
- const spaces: Space[] = buildSpaces();
- const uiCapabilities: UICapabilities = buildUICapabilities();
-
- const wrapper = mountWithIntl(
-
- );
-
- expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1);
- expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0);
- expectSaveFormButtons(wrapper);
- });
-
- it('renders an auth error when not authorized to manage spaces', () => {
- const role: Role = {
- name: 'my custom role',
- metadata: {},
- elasticsearch: {
- cluster: ['all'],
- indices: [],
- run_as: ['*'],
- },
- kibana: [
- {
- spaces: ['*'],
- base: ['all'],
- feature: {},
- },
- ],
- };
-
- const features: Feature[] = buildFeatures();
- const mockHttpClient = jest.fn();
- const indexPatterns: string[] = ['foo*', 'bar*'];
- const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges();
- const builtinESPrivileges = buildBuiltinESPrivileges();
- const spaces: Space[] = buildSpaces();
- const uiCapabilities: UICapabilities = buildUICapabilities(false);
-
- const wrapper = mountWithIntl(
-
- );
-
- expect(wrapper.find('[data-test-subj="reservedRoleBadgeTooltip"]')).toHaveLength(0);
-
- expect(
- wrapper.find('EuiCallOut[data-test-subj="userCannotManageSpacesCallout"]')
- ).toHaveLength(1);
-
- expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1);
- expectSaveFormButtons(wrapper);
- });
-
- it('renders a partial read-only view when there is a transform error', () => {
- const role: Role = {
- name: 'my custom role',
- metadata: {},
- elasticsearch: {
- cluster: ['all'],
- indices: [],
- run_as: ['*'],
- },
- kibana: [],
- _transform_error: ['kibana'],
- };
-
- const features: Feature[] = buildFeatures();
- const mockHttpClient = jest.fn();
- const indexPatterns: string[] = ['foo*', 'bar*'];
- const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges();
- const builtinESPrivileges = buildBuiltinESPrivileges();
- const spaces: Space[] = buildSpaces();
- const uiCapabilities: UICapabilities = buildUICapabilities(false);
-
- const wrapper = mountWithIntl(
-
- );
-
- expect(wrapper.find(TransformErrorSection)).toHaveLength(1);
- expectReadOnlyFormButtons(wrapper);
- });
- });
-
- describe('with spaces disabled', () => {
- it('can render a reserved role', () => {
- const role: Role = {
- name: 'superuser',
- metadata: {
- _reserved: true,
- },
- elasticsearch: {
- cluster: ['all'],
- indices: [],
- run_as: ['*'],
- },
- kibana: [
- {
- spaces: ['*'],
- base: ['all'],
- feature: {},
- },
- ],
- };
-
- const features: Feature[] = buildFeatures();
- const mockHttpClient = jest.fn();
- const indexPatterns: string[] = ['foo*', 'bar*'];
- const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges();
- const builtinESPrivileges = buildBuiltinESPrivileges();
- const uiCapabilities: UICapabilities = buildUICapabilities();
-
- const wrapper = mountWithIntl(
-
- );
-
- expect(wrapper.find('[data-test-subj="reservedRoleBadgeTooltip"]')).toHaveLength(1);
- expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1);
- expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0);
- expectReadOnlyFormButtons(wrapper);
- });
-
- it('can render a user defined role', () => {
- const role: Role = {
- name: 'my custom role',
- metadata: {},
- elasticsearch: {
- cluster: ['all'],
- indices: [],
- run_as: ['*'],
- },
- kibana: [
- {
- spaces: ['*'],
- base: ['all'],
- feature: {},
- },
- ],
- };
-
- const features: Feature[] = buildFeatures();
- const mockHttpClient = jest.fn();
- const indexPatterns: string[] = ['foo*', 'bar*'];
- const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges();
- const builtinESPrivileges = buildBuiltinESPrivileges();
- const uiCapabilities: UICapabilities = buildUICapabilities();
-
- const wrapper = mountWithIntl(
-
- );
-
- expect(wrapper.find('[data-test-subj="reservedRoleBadgeTooltip"]')).toHaveLength(0);
- expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1);
- expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0);
- expectSaveFormButtons(wrapper);
- });
-
- it('can render when creating a new role', () => {
- // @ts-ignore
- const role: Role = {
- metadata: {},
- elasticsearch: {
- cluster: [],
- indices: [],
- run_as: [],
- },
- kibana: [],
- };
-
- const features: Feature[] = buildFeatures();
- const mockHttpClient = jest.fn();
- const indexPatterns: string[] = ['foo*', 'bar*'];
- const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges();
- const builtinESPrivileges = buildBuiltinESPrivileges();
- const uiCapabilities: UICapabilities = buildUICapabilities();
-
- const wrapper = mountWithIntl(
-
- );
-
- expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1);
- expectSaveFormButtons(wrapper);
- });
-
- it('can render when cloning an existing role', () => {
- const role: Role = {
- metadata: {
- _reserved: false,
- },
- name: '',
- elasticsearch: {
- cluster: ['all', 'manage'],
- indices: [
- {
- names: ['foo*'],
- privileges: ['all'],
- field_security: {
- except: ['f'],
- grant: ['b*'],
- },
- },
- ],
- run_as: ['elastic'],
- },
- kibana: [
- {
- spaces: ['*'],
- base: ['all'],
- feature: {},
- },
- ],
- };
-
- const features: Feature[] = buildFeatures();
- const mockHttpClient = jest.fn();
- const indexPatterns: string[] = ['foo*', 'bar*'];
- const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges();
- const builtinESPrivileges = buildBuiltinESPrivileges();
- const uiCapabilities: UICapabilities = buildUICapabilities();
-
- const wrapper = mountWithIntl(
-
- );
-
- expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1);
- expectSaveFormButtons(wrapper);
- });
-
- it('does not care if user cannot manage spaces', () => {
- const role: Role = {
- name: 'my custom role',
- metadata: {},
- elasticsearch: {
- cluster: ['all'],
- indices: [],
- run_as: ['*'],
- },
- kibana: [
- {
- spaces: ['*'],
- base: ['all'],
- feature: {},
- },
- ],
- };
-
- const features: Feature[] = buildFeatures();
- const mockHttpClient = jest.fn();
- const indexPatterns: string[] = ['foo*', 'bar*'];
- const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges();
- const builtinESPrivileges = buildBuiltinESPrivileges();
- const uiCapabilities: UICapabilities = buildUICapabilities(false);
-
- const wrapper = mountWithIntl(
-
- );
-
- expect(wrapper.find('[data-test-subj="reservedRoleBadgeTooltip"]')).toHaveLength(0);
-
- expect(
- wrapper.find('EuiCallOut[data-test-subj="userCannotManageSpacesCallout"]')
- ).toHaveLength(0);
-
- expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1);
- expectSaveFormButtons(wrapper);
- });
-
- it('renders a partial read-only view when there is a transform error', () => {
- const role: Role = {
- name: 'my custom role',
- metadata: {},
- elasticsearch: {
- cluster: ['all'],
- indices: [],
- run_as: ['*'],
- },
- kibana: [],
- _transform_error: ['kibana'],
- };
-
- const features: Feature[] = buildFeatures();
- const mockHttpClient = jest.fn();
- const indexPatterns: string[] = ['foo*', 'bar*'];
- const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges();
- const builtinESPrivileges = buildBuiltinESPrivileges();
- const uiCapabilities: UICapabilities = buildUICapabilities(false);
-
- const wrapper = mountWithIntl(
-
- );
-
- expect(wrapper.find(TransformErrorSection)).toHaveLength(1);
- expectReadOnlyFormButtons(wrapper);
- });
- });
-});
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.tsx
deleted file mode 100644
index 2ba012afa689d..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.tsx
+++ /dev/null
@@ -1,409 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import {
- EuiButton,
- EuiButtonEmpty,
- EuiFieldText,
- EuiFlexGroup,
- EuiFlexItem,
- EuiForm,
- EuiFormRow,
- EuiPanel,
- EuiSpacer,
- EuiText,
- EuiTitle,
-} from '@elastic/eui';
-import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
-import { get } from 'lodash';
-import React, { ChangeEvent, Component, Fragment, HTMLProps } from 'react';
-import { UICapabilities } from 'ui/capabilities';
-import { toastNotifications } from 'ui/notify';
-import { Space } from '../../../../../../spaces/common/model/space';
-import { Feature } from '../../../../../../../../plugins/features/public';
-import {
- KibanaPrivileges,
- RawKibanaPrivileges,
- Role,
- BuiltinESPrivileges,
-} from '../../../../../common/model';
-import {
- isReadOnlyRole,
- isReservedRole,
- copyRole,
- prepareRoleClone,
-} from '../../../../lib/role_utils';
-import { deleteRole, saveRole } from '../../../../objects';
-import { ROLES_PATH } from '../../management_urls';
-import { RoleValidationResult, RoleValidator } from '../lib/validate_role';
-import { DeleteRoleButton } from './delete_role_button';
-import { ElasticsearchPrivileges, KibanaPrivilegesRegion } from './privileges';
-import { ReservedRoleBadge } from './reserved_role_badge';
-
-interface Props {
- action: 'edit' | 'clone';
- role: Role;
- runAsUsers: string[];
- indexPatterns: string[];
- httpClient: any;
- allowDocumentLevelSecurity: boolean;
- allowFieldLevelSecurity: boolean;
- kibanaPrivileges: RawKibanaPrivileges;
- builtinESPrivileges: BuiltinESPrivileges;
- spaces?: Space[];
- spacesEnabled: boolean;
- intl: InjectedIntl;
- uiCapabilities: UICapabilities;
- features: Feature[];
-}
-
-interface State {
- role: Role;
- formError: RoleValidationResult | null;
-}
-
-class EditRolePageUI extends Component {
- private validator: RoleValidator;
-
- constructor(props: Props) {
- super(props);
-
- this.validator = new RoleValidator({ shouldValidate: false });
-
- let role: Role;
- if (props.action === 'clone') {
- role = prepareRoleClone(props.role);
- } else {
- role = copyRole(props.role);
- }
-
- this.state = {
- role,
- formError: null,
- };
- }
-
- public componentDidMount() {
- if (this.props.action === 'clone' && isReservedRole(this.props.role)) {
- this.backToRoleList();
- }
- }
-
- public render() {
- const description = this.props.spacesEnabled ? (
-
- ) : (
-
- );
-
- return (
-
-
- {this.getFormTitle()}
-
-
-
- {description}
-
- {isReservedRole(this.state.role) && (
-
-
-
-
-
-
-
-
- )}
-
-
-
- {this.getRoleName()}
-
- {this.getElasticsearchPrivileges()}
-
- {this.getKibanaPrivileges()}
-
-
-
- {this.getFormButtons()}
-
-
- );
- }
-
- private getFormTitle = () => {
- let titleText;
- const props: HTMLProps = {
- tabIndex: 0,
- };
- if (isReservedRole(this.state.role)) {
- titleText = (
-
- );
- props['aria-describedby'] = 'reservedRoleDescription';
- } else if (this.editingExistingRole()) {
- titleText = (
-
- );
- } else {
- titleText = (
-
- );
- }
-
- return (
-
-
- {titleText}
-
-
- );
- };
-
- private getActionButton = () => {
- if (this.editingExistingRole() && !isReadOnlyRole(this.state.role)) {
- return (
-
-
-
- );
- }
-
- return null;
- };
-
- private getRoleName = () => {
- return (
-
-
- }
- helpText={
- !isReservedRole(this.state.role) && this.editingExistingRole() ? (
-
- ) : (
- undefined
- )
- }
- {...this.validator.validateRoleName(this.state.role)}
- >
-
-
-
- );
- };
-
- private onNameChange = (e: ChangeEvent) => {
- const rawValue = e.target.value;
- const name = rawValue.replace(/\s/g, '_');
-
- this.setState({
- role: {
- ...this.state.role,
- name,
- },
- });
- };
-
- private getElasticsearchPrivileges() {
- return (
-
-
-
-
- );
- }
-
- private onRoleChange = (role: Role) => {
- this.setState({
- role,
- });
- };
-
- private getKibanaPrivileges = () => {
- return (
-
-
-
-
- );
- };
-
- private getFormButtons = () => {
- if (isReadOnlyRole(this.state.role)) {
- return this.getReturnToRoleListButton();
- }
-
- return (
-
- {this.getSaveButton()}
- {this.getCancelButton()}
-
- {this.getActionButton()}
-
- );
- };
-
- private getReturnToRoleListButton = () => {
- return (
-
-
-
- );
- };
-
- private getSaveButton = () => {
- const saveText = this.editingExistingRole() ? (
-
- ) : (
-
- );
-
- return (
-
- {saveText}
-
- );
- };
-
- private getCancelButton = () => {
- return (
-
-
-
- );
- };
-
- private editingExistingRole = () => {
- return !!this.props.role.name && this.props.action === 'edit';
- };
-
- private saveRole = () => {
- this.validator.enableValidation();
-
- const result = this.validator.validateForSave(this.state.role);
- if (result.isInvalid) {
- this.setState({
- formError: result,
- });
- } else {
- this.setState({
- formError: null,
- });
-
- const { httpClient, intl, spacesEnabled } = this.props;
-
- saveRole(httpClient, this.state.role, spacesEnabled)
- .then(() => {
- toastNotifications.addSuccess(
- intl.formatMessage({
- id: 'xpack.security.management.editRole.roleSuccessfullySavedNotificationMessage',
- defaultMessage: 'Saved role',
- })
- );
- this.backToRoleList();
- })
- .catch((error: any) => {
- toastNotifications.addDanger(get(error, 'data.message'));
- });
- }
- };
-
- private handleDeleteRole = () => {
- const { httpClient, role, intl } = this.props;
-
- deleteRole(httpClient, role.name)
- .then(() => {
- toastNotifications.addSuccess(
- intl.formatMessage({
- id: 'xpack.security.management.editRole.roleSuccessfullyDeletedNotificationMessage',
- defaultMessage: 'Deleted role',
- })
- );
- this.backToRoleList();
- })
- .catch((error: any) => {
- toastNotifications.addDanger(get(error, 'data.message'));
- });
- };
-
- private backToRoleList = () => {
- window.location.hash = ROLES_PATH;
- };
-}
-
-export const EditRolePage = injectI18n(EditRolePageUI);
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/elasticsearch_privileges.test.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/elasticsearch_privileges.test.tsx
deleted file mode 100644
index 5ba3d1daf61ac..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/elasticsearch_privileges.test.tsx
+++ /dev/null
@@ -1,96 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
-import { RoleValidator } from '../../../lib/validate_role';
-import { ClusterPrivileges } from './cluster_privileges';
-import { ElasticsearchPrivileges } from './elasticsearch_privileges';
-import { IndexPrivileges } from './index_privileges';
-
-test('it renders without crashing', () => {
- const props = {
- role: {
- name: '',
- elasticsearch: {
- cluster: [],
- indices: [],
- run_as: [],
- },
- kibana: [],
- },
- editable: true,
- httpClient: jest.fn(),
- onChange: jest.fn(),
- runAsUsers: [],
- indexPatterns: [],
- allowDocumentLevelSecurity: true,
- allowFieldLevelSecurity: true,
- validator: new RoleValidator(),
- builtinESPrivileges: {
- cluster: ['all', 'manage', 'monitor'],
- index: ['all', 'read', 'write', 'index'],
- },
- };
- const wrapper = shallowWithIntl();
- expect(wrapper).toMatchSnapshot();
-});
-
-test('it renders ClusterPrivileges', () => {
- const props = {
- role: {
- name: '',
- elasticsearch: {
- cluster: [],
- indices: [],
- run_as: [],
- },
- kibana: [],
- },
- editable: true,
- httpClient: jest.fn(),
- onChange: jest.fn(),
- runAsUsers: [],
- indexPatterns: [],
- allowDocumentLevelSecurity: true,
- allowFieldLevelSecurity: true,
- validator: new RoleValidator(),
- builtinESPrivileges: {
- cluster: ['all', 'manage', 'monitor'],
- index: ['all', 'read', 'write', 'index'],
- },
- };
- const wrapper = mountWithIntl();
- expect(wrapper.find(ClusterPrivileges)).toHaveLength(1);
-});
-
-test('it renders IndexPrivileges', () => {
- const props = {
- role: {
- name: '',
- elasticsearch: {
- cluster: [],
- indices: [],
- run_as: [],
- },
- kibana: [],
- },
- editable: true,
- httpClient: jest.fn(),
- onChange: jest.fn(),
- runAsUsers: [],
- indexPatterns: [],
- allowDocumentLevelSecurity: true,
- allowFieldLevelSecurity: true,
- validator: new RoleValidator(),
- builtinESPrivileges: {
- cluster: ['all', 'manage', 'monitor'],
- index: ['all', 'read', 'write', 'index'],
- },
- };
- const wrapper = mountWithIntl();
- expect(wrapper.find(IndexPrivileges)).toHaveLength(1);
-});
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/edit_role.html b/x-pack/legacy/plugins/security/public/views/management/edit_role/edit_role.html
deleted file mode 100644
index ca4073dcad6f5..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/edit_role.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js b/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js
deleted file mode 100644
index 27c9beb4ba828..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js
+++ /dev/null
@@ -1,176 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import _ from 'lodash';
-import routes from 'ui/routes';
-import { capabilities } from 'ui/capabilities';
-import { kfetch } from 'ui/kfetch';
-import { fatalError, toastNotifications } from 'ui/notify';
-import { npStart } from 'ui/new_platform';
-import template from 'plugins/security/views/management/edit_role/edit_role.html';
-import 'plugins/security/services/shield_role';
-import 'plugins/security/services/shield_indices';
-import { xpackInfo } from 'plugins/xpack_main/services/xpack_info';
-import { UserAPIClient } from '../../../lib/api';
-import { ROLES_PATH, CLONE_ROLES_PATH, EDIT_ROLES_PATH } from '../management_urls';
-import { getEditRoleBreadcrumbs, getCreateRoleBreadcrumbs } from '../breadcrumbs';
-
-import { EditRolePage } from './components';
-
-import React from 'react';
-import { render, unmountComponentAtNode } from 'react-dom';
-import { I18nContext } from 'ui/i18n';
-import { i18n } from '@kbn/i18n';
-
-const routeDefinition = action => ({
- template,
- k7Breadcrumbs: ($injector, $route) =>
- $injector.invoke(
- action === 'edit' && $route.current.params.name
- ? getEditRoleBreadcrumbs
- : getCreateRoleBreadcrumbs
- ),
- resolve: {
- role($route, ShieldRole, Promise, kbnUrl) {
- const name = $route.current.params.name;
-
- let role;
-
- if (name != null) {
- role = ShieldRole.get({ name }).$promise.catch(response => {
- if (response.status === 404) {
- toastNotifications.addDanger({
- title: i18n.translate('xpack.security.management.roles.roleNotFound', {
- defaultMessage: 'No "{roleName}" role found.',
- values: { roleName: name },
- }),
- });
- kbnUrl.redirect(ROLES_PATH);
- } else {
- return fatalError(response);
- }
- });
- } else {
- role = Promise.resolve(
- new ShieldRole({
- elasticsearch: {
- cluster: [],
- indices: [],
- run_as: [],
- },
- kibana: [],
- _unrecognized_applications: [],
- })
- );
- }
-
- return role.then(res => res.toJSON());
- },
- users() {
- return new UserAPIClient().getUsers().then(users => _.map(users, 'username'));
- },
- indexPatterns() {
- return npStart.plugins.data.indexPatterns.getTitles();
- },
- spaces(spacesEnabled) {
- if (spacesEnabled) {
- return kfetch({ method: 'get', pathname: '/api/spaces/space' });
- }
- return [];
- },
- kibanaPrivileges() {
- return kfetch({
- method: 'get',
- pathname: '/api/security/privileges',
- query: { includeActions: true },
- });
- },
- builtinESPrivileges() {
- return kfetch({ method: 'get', pathname: '/internal/security/esPrivileges/builtin' });
- },
- features() {
- return kfetch({ method: 'get', pathname: '/api/features' }).catch(e => {
- // TODO: This check can be removed once all of these `resolve` entries are moved out of Angular and into the React app.
- const unauthorizedForFeatures = _.get(e, 'body.statusCode') === 404;
- if (unauthorizedForFeatures) {
- return [];
- }
- throw e;
- });
- },
- },
- controllerAs: 'editRole',
- controller($injector, $scope, $http, enableSpaceAwarePrivileges) {
- const $route = $injector.get('$route');
- const role = $route.current.locals.role;
-
- const allowDocumentLevelSecurity = xpackInfo.get(
- 'features.security.allowRoleDocumentLevelSecurity'
- );
- const allowFieldLevelSecurity = xpackInfo.get('features.security.allowRoleFieldLevelSecurity');
- if (role.elasticsearch.indices.length === 0) {
- const emptyOption = {
- names: [],
- privileges: [],
- };
-
- if (allowFieldLevelSecurity) {
- emptyOption.field_security = {
- grant: ['*'],
- except: [],
- };
- }
-
- if (allowDocumentLevelSecurity) {
- emptyOption.query = '';
- }
-
- role.elasticsearch.indices.push(emptyOption);
- }
-
- const {
- users,
- indexPatterns,
- spaces,
- kibanaPrivileges,
- builtinESPrivileges,
- features,
- } = $route.current.locals;
-
- $scope.$$postDigest(async () => {
- const domNode = document.getElementById('editRoleReactRoot');
-
- render(
-
-
- ,
- domNode
- );
-
- // unmount react on controller destroy
- $scope.$on('$destroy', () => {
- unmountComponentAtNode(domNode);
- });
- });
- },
-});
-
-routes.when(`${CLONE_ROLES_PATH}/:name`, routeDefinition('clone'));
-routes.when(`${EDIT_ROLES_PATH}/:name?`, routeDefinition('edit'));
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_user/_index.scss b/x-pack/legacy/plugins/security/public/views/management/edit_user/_index.scss
deleted file mode 100644
index c5da74aa3f785..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/edit_user/_index.scss
+++ /dev/null
@@ -1 +0,0 @@
-@import './users';
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_user/edit_user.html b/x-pack/legacy/plugins/security/public/views/management/edit_user/edit_user.html
deleted file mode 100644
index 4fa2768480874..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/edit_user/edit_user.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_user/edit_user.js b/x-pack/legacy/plugins/security/public/views/management/edit_user/edit_user.js
deleted file mode 100644
index ab218022c6ee6..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/edit_user/edit_user.js
+++ /dev/null
@@ -1,58 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-import routes from 'ui/routes';
-import template from 'plugins/security/views/management/edit_user/edit_user.html';
-import 'angular-resource';
-import 'ui/angular_ui_select';
-import 'plugins/security/services/shield_role';
-import { EDIT_USERS_PATH } from '../management_urls';
-import { EditUserPage } from './components';
-import { UserAPIClient } from '../../../lib/api';
-import React from 'react';
-import { render, unmountComponentAtNode } from 'react-dom';
-import { I18nContext } from 'ui/i18n';
-import { npSetup } from 'ui/new_platform';
-import { getEditUserBreadcrumbs, getCreateUserBreadcrumbs } from '../breadcrumbs';
-
-const renderReact = (elem, changeUrl, username) => {
- render(
-
-
- ,
- elem
- );
-};
-
-routes.when(`${EDIT_USERS_PATH}/:username?`, {
- template,
- k7Breadcrumbs: ($injector, $route) =>
- $injector.invoke(
- $route.current.params.username ? getEditUserBreadcrumbs : getCreateUserBreadcrumbs
- ),
- controllerAs: 'editUser',
- controller($scope, $route, kbnUrl) {
- $scope.$on('$destroy', () => {
- const elem = document.getElementById('editUserReactRoot');
- if (elem) {
- unmountComponentAtNode(elem);
- }
- });
- $scope.$$postDigest(() => {
- const elem = document.getElementById('editUserReactRoot');
- const username = $route.current.params.username;
- const changeUrl = url => {
- kbnUrl.change(url);
- $scope.$apply();
- };
- renderReact(elem, changeUrl, username);
- });
- },
-});
diff --git a/x-pack/legacy/plugins/security/public/views/management/management.js b/x-pack/legacy/plugins/security/public/views/management/management.js
deleted file mode 100644
index f0369f232aeba..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/management.js
+++ /dev/null
@@ -1,134 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import 'plugins/security/views/management/change_password_form/change_password_form';
-import 'plugins/security/views/management/password_form/password_form';
-import 'plugins/security/views/management/users_grid/users';
-import 'plugins/security/views/management/roles_grid/roles';
-import 'plugins/security/views/management/api_keys_grid/api_keys';
-import 'plugins/security/views/management/edit_user/edit_user';
-import 'plugins/security/views/management/edit_role/index';
-import 'plugins/security/views/management/role_mappings/role_mappings_grid';
-import 'plugins/security/views/management/role_mappings/edit_role_mapping';
-import routes from 'ui/routes';
-import { xpackInfo } from 'plugins/xpack_main/services/xpack_info';
-import { ROLES_PATH, USERS_PATH, API_KEYS_PATH, ROLE_MAPPINGS_PATH } from './management_urls';
-
-import { management } from 'ui/management';
-import { npSetup } from 'ui/new_platform';
-import { i18n } from '@kbn/i18n';
-import { toastNotifications } from 'ui/notify';
-
-routes
- .defaults(/^\/management\/security(\/|$)/, {
- resolve: {
- showLinks(kbnUrl, Promise) {
- if (!xpackInfo.get('features.security.showLinks')) {
- toastNotifications.addDanger({
- title: xpackInfo.get('features.security.linksMessage'),
- });
- kbnUrl.redirect('/management');
- return Promise.halt();
- }
- },
- },
- })
- .defaults(/\/management/, {
- resolve: {
- securityManagementSection: function() {
- const showSecurityLinks = xpackInfo.get('features.security.showLinks');
- const showRoleMappingsManagementLink = xpackInfo.get(
- 'features.security.showRoleMappingsManagement'
- );
-
- function deregisterSecurity() {
- management.deregister('security');
- }
-
- function deregisterRoleMappingsManagement() {
- if (management.hasItem('security')) {
- const security = management.getSection('security');
- if (security.hasItem('roleMappings')) {
- security.deregister('roleMappings');
- }
- }
- }
-
- function ensureSecurityRegistered() {
- const registerSecurity = () =>
- management.register('security', {
- display: i18n.translate('xpack.security.management.securityTitle', {
- defaultMessage: 'Security',
- }),
- order: 100,
- icon: 'securityApp',
- });
- const getSecurity = () => management.getSection('security');
-
- const security = management.hasItem('security') ? getSecurity() : registerSecurity();
-
- if (!security.hasItem('users')) {
- security.register('users', {
- name: 'securityUsersLink',
- order: 10,
- display: i18n.translate('xpack.security.management.usersTitle', {
- defaultMessage: 'Users',
- }),
- url: `#${USERS_PATH}`,
- });
- }
-
- if (!security.hasItem('roles')) {
- security.register('roles', {
- name: 'securityRolesLink',
- order: 20,
- display: i18n.translate('xpack.security.management.rolesTitle', {
- defaultMessage: 'Roles',
- }),
- url: `#${ROLES_PATH}`,
- });
- }
-
- if (!security.hasItem('apiKeys')) {
- security.register('apiKeys', {
- name: 'securityApiKeysLink',
- order: 30,
- display: i18n.translate('xpack.security.management.apiKeysTitle', {
- defaultMessage: 'API Keys',
- }),
- url: `#${API_KEYS_PATH}`,
- });
- }
-
- if (showRoleMappingsManagementLink && !security.hasItem('roleMappings')) {
- security.register('roleMappings', {
- name: 'securityRoleMappingLink',
- order: 30,
- display: i18n.translate('xpack.security.management.roleMappingsTitle', {
- defaultMessage: 'Role Mappings',
- }),
- url: `#${ROLE_MAPPINGS_PATH}`,
- });
- }
- }
-
- if (!showSecurityLinks) {
- deregisterSecurity();
- } else {
- if (!showRoleMappingsManagementLink) {
- deregisterRoleMappingsManagement();
- }
-
- // getCurrentUser will reject if there is no authenticated user, so we prevent them from
- // seeing the security management screens.
- return npSetup.plugins.security.authc
- .getCurrentUser()
- .then(ensureSecurityRegistered)
- .catch(deregisterSecurity);
- }
- },
- },
- });
diff --git a/x-pack/legacy/plugins/security/public/views/management/password_form/password_form.html b/x-pack/legacy/plugins/security/public/views/management/password_form/password_form.html
deleted file mode 100644
index 72956992100f5..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/password_form/password_form.html
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
-
-
-
-
diff --git a/x-pack/legacy/plugins/security/public/views/management/password_form/password_form.js b/x-pack/legacy/plugins/security/public/views/management/password_form/password_form.js
deleted file mode 100644
index edcccdb5e6e69..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/password_form/password_form.js
+++ /dev/null
@@ -1,24 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { uiModules } from 'ui/modules';
-import template from './password_form.html';
-
-const module = uiModules.get('security', ['kibana']);
-module.directive('kbnPasswordForm', function() {
- return {
- template,
- scope: {
- password: '=',
- },
- restrict: 'E',
- replace: true,
- controllerAs: 'passwordController',
- controller: function() {
- this.confirmation = null;
- },
- };
-});
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/_index.scss b/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/_index.scss
deleted file mode 100644
index 80e08ebcf1226..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/_index.scss
+++ /dev/null
@@ -1 +0,0 @@
-@import './components/rule_editor_panel/index';
\ No newline at end of file
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/edit_role_mapping_page.test.tsx b/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/edit_role_mapping_page.test.tsx
deleted file mode 100644
index 375a8d9f374a8..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/edit_role_mapping_page.test.tsx
+++ /dev/null
@@ -1,341 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers';
-import { findTestSubject } from 'test_utils/find_test_subject';
-
-// brace/ace uses the Worker class, which is not currently provided by JSDOM.
-// This is not required for the tests to pass, but it rather suppresses lengthy
-// warnings in the console which adds unnecessary noise to the test output.
-import 'test_utils/stub_web_worker';
-
-import { RoleMappingsAPI } from '../../../../../lib/role_mappings_api';
-import { EditRoleMappingPage } from '.';
-import { NoCompatibleRealms, SectionLoading, PermissionDenied } from '../../components';
-import { VisualRuleEditor } from './rule_editor_panel/visual_rule_editor';
-import { JSONRuleEditor } from './rule_editor_panel/json_rule_editor';
-import { EuiComboBox } from '@elastic/eui';
-
-jest.mock('../../../../../lib/roles_api', () => {
- return {
- RolesApi: {
- getRoles: () => Promise.resolve([{ name: 'foo_role' }, { name: 'bar role' }]),
- },
- };
-});
-
-describe('EditRoleMappingPage', () => {
- it('allows a role mapping to be created', async () => {
- const roleMappingsAPI = ({
- saveRoleMapping: jest.fn().mockResolvedValue(null),
- checkRoleMappingFeatures: jest.fn().mockResolvedValue({
- canManageRoleMappings: true,
- hasCompatibleRealms: true,
- canUseInlineScripts: true,
- canUseStoredScripts: true,
- }),
- } as unknown) as RoleMappingsAPI;
-
- const wrapper = mountWithIntl();
-
- await nextTick();
- wrapper.update();
-
- findTestSubject(wrapper, 'roleMappingFormNameInput').simulate('change', {
- target: { value: 'my-role-mapping' },
- });
-
- (wrapper
- .find(EuiComboBox)
- .filter('[data-test-subj="roleMappingFormRoleComboBox"]')
- .props() as any).onChange([{ label: 'foo_role' }]);
-
- findTestSubject(wrapper, 'roleMappingsAddRuleButton').simulate('click');
-
- findTestSubject(wrapper, 'saveRoleMappingButton').simulate('click');
-
- expect(roleMappingsAPI.saveRoleMapping).toHaveBeenCalledWith({
- name: 'my-role-mapping',
- enabled: true,
- roles: ['foo_role'],
- role_templates: [],
- rules: {
- all: [{ field: { username: '*' } }],
- },
- metadata: {},
- });
- });
-
- it('allows a role mapping to be updated', async () => {
- const roleMappingsAPI = ({
- saveRoleMapping: jest.fn().mockResolvedValue(null),
- getRoleMapping: jest.fn().mockResolvedValue({
- name: 'foo',
- role_templates: [
- {
- template: { id: 'foo' },
- },
- ],
- enabled: true,
- rules: {
- any: [{ field: { 'metadata.someCustomOption': [false, true, 'asdf'] } }],
- },
- metadata: {
- foo: 'bar',
- bar: 'baz',
- },
- }),
- checkRoleMappingFeatures: jest.fn().mockResolvedValue({
- canManageRoleMappings: true,
- hasCompatibleRealms: true,
- canUseInlineScripts: true,
- canUseStoredScripts: true,
- }),
- } as unknown) as RoleMappingsAPI;
-
- const wrapper = mountWithIntl(
-
- );
-
- await nextTick();
- wrapper.update();
-
- findTestSubject(wrapper, 'switchToRolesButton').simulate('click');
-
- (wrapper
- .find(EuiComboBox)
- .filter('[data-test-subj="roleMappingFormRoleComboBox"]')
- .props() as any).onChange([{ label: 'foo_role' }]);
-
- findTestSubject(wrapper, 'roleMappingsAddRuleButton').simulate('click');
- wrapper.find('button[id="addRuleOption"]').simulate('click');
-
- findTestSubject(wrapper, 'saveRoleMappingButton').simulate('click');
-
- expect(roleMappingsAPI.saveRoleMapping).toHaveBeenCalledWith({
- name: 'foo',
- enabled: true,
- roles: ['foo_role'],
- role_templates: [],
- rules: {
- any: [
- { field: { 'metadata.someCustomOption': [false, true, 'asdf'] } },
- { field: { username: '*' } },
- ],
- },
- metadata: {
- foo: 'bar',
- bar: 'baz',
- },
- });
- });
-
- it('renders a permission denied message when unauthorized to manage role mappings', async () => {
- const roleMappingsAPI = ({
- checkRoleMappingFeatures: jest.fn().mockResolvedValue({
- canManageRoleMappings: false,
- hasCompatibleRealms: true,
- }),
- } as unknown) as RoleMappingsAPI;
-
- const wrapper = mountWithIntl();
- expect(wrapper.find(SectionLoading)).toHaveLength(1);
- expect(wrapper.find(PermissionDenied)).toHaveLength(0);
-
- await nextTick();
- wrapper.update();
-
- expect(wrapper.find(SectionLoading)).toHaveLength(0);
- expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0);
- expect(wrapper.find(PermissionDenied)).toHaveLength(1);
- });
-
- it('renders a warning when there are no compatible realms enabled', async () => {
- const roleMappingsAPI = ({
- checkRoleMappingFeatures: jest.fn().mockResolvedValue({
- canManageRoleMappings: true,
- hasCompatibleRealms: false,
- }),
- } as unknown) as RoleMappingsAPI;
-
- const wrapper = mountWithIntl();
- expect(wrapper.find(SectionLoading)).toHaveLength(1);
- expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0);
-
- await nextTick();
- wrapper.update();
-
- expect(wrapper.find(SectionLoading)).toHaveLength(0);
- expect(wrapper.find(NoCompatibleRealms)).toHaveLength(1);
- });
-
- it('renders a warning when editing a mapping with a stored role template, when stored scripts are disabled', async () => {
- const roleMappingsAPI = ({
- getRoleMapping: jest.fn().mockResolvedValue({
- name: 'foo',
- role_templates: [
- {
- template: { id: 'foo' },
- },
- ],
- enabled: true,
- rules: {
- field: { username: '*' },
- },
- }),
- checkRoleMappingFeatures: jest.fn().mockResolvedValue({
- canManageRoleMappings: true,
- hasCompatibleRealms: true,
- canUseInlineScripts: true,
- canUseStoredScripts: false,
- }),
- } as unknown) as RoleMappingsAPI;
-
- const wrapper = mountWithIntl(
-
- );
-
- expect(findTestSubject(wrapper, 'roleMappingInlineScriptsDisabled')).toHaveLength(0);
- expect(findTestSubject(wrapper, 'roleMappingStoredScriptsDisabled')).toHaveLength(0);
-
- await nextTick();
- wrapper.update();
-
- expect(findTestSubject(wrapper, 'roleMappingInlineScriptsDisabled')).toHaveLength(0);
- expect(findTestSubject(wrapper, 'roleMappingStoredScriptsDisabled')).toHaveLength(1);
- });
-
- it('renders a warning when editing a mapping with an inline role template, when inline scripts are disabled', async () => {
- const roleMappingsAPI = ({
- getRoleMapping: jest.fn().mockResolvedValue({
- name: 'foo',
- role_templates: [
- {
- template: { source: 'foo' },
- },
- ],
- enabled: true,
- rules: {
- field: { username: '*' },
- },
- }),
- checkRoleMappingFeatures: jest.fn().mockResolvedValue({
- canManageRoleMappings: true,
- hasCompatibleRealms: true,
- canUseInlineScripts: false,
- canUseStoredScripts: true,
- }),
- } as unknown) as RoleMappingsAPI;
-
- const wrapper = mountWithIntl(
-
- );
-
- expect(findTestSubject(wrapper, 'roleMappingInlineScriptsDisabled')).toHaveLength(0);
- expect(findTestSubject(wrapper, 'roleMappingStoredScriptsDisabled')).toHaveLength(0);
-
- await nextTick();
- wrapper.update();
-
- expect(findTestSubject(wrapper, 'roleMappingInlineScriptsDisabled')).toHaveLength(1);
- expect(findTestSubject(wrapper, 'roleMappingStoredScriptsDisabled')).toHaveLength(0);
- });
-
- it('renders the visual editor by default for simple rule sets', async () => {
- const roleMappingsAPI = ({
- getRoleMapping: jest.fn().mockResolvedValue({
- name: 'foo',
- roles: ['superuser'],
- enabled: true,
- rules: {
- all: [
- {
- field: {
- username: '*',
- },
- },
- {
- field: {
- dn: null,
- },
- },
- {
- field: {
- realm: ['ldap', 'pki', null, 12],
- },
- },
- ],
- },
- }),
- checkRoleMappingFeatures: jest.fn().mockResolvedValue({
- canManageRoleMappings: true,
- hasCompatibleRealms: true,
- canUseInlineScripts: true,
- canUseStoredScripts: true,
- }),
- } as unknown) as RoleMappingsAPI;
-
- const wrapper = mountWithIntl(
-
- );
-
- await nextTick();
- wrapper.update();
-
- expect(wrapper.find(VisualRuleEditor)).toHaveLength(1);
- expect(wrapper.find(JSONRuleEditor)).toHaveLength(0);
- });
-
- it('renders the JSON editor by default for complex rule sets', async () => {
- const createRule = (depth: number): Record => {
- if (depth > 0) {
- const rule = {
- all: [
- {
- field: {
- username: '*',
- },
- },
- ],
- } as Record;
-
- const subRule = createRule(depth - 1);
- if (subRule) {
- rule.all.push(subRule);
- }
-
- return rule;
- }
- return null as any;
- };
-
- const roleMappingsAPI = ({
- getRoleMapping: jest.fn().mockResolvedValue({
- name: 'foo',
- roles: ['superuser'],
- enabled: true,
- rules: createRule(10),
- }),
- checkRoleMappingFeatures: jest.fn().mockResolvedValue({
- canManageRoleMappings: true,
- hasCompatibleRealms: true,
- canUseInlineScripts: true,
- canUseStoredScripts: true,
- }),
- } as unknown) as RoleMappingsAPI;
-
- const wrapper = mountWithIntl(
-
- );
-
- await nextTick();
- wrapper.update();
-
- expect(wrapper.find(VisualRuleEditor)).toHaveLength(0);
- expect(wrapper.find(JSONRuleEditor)).toHaveLength(1);
- });
-});
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/edit_role_mapping.html b/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/edit_role_mapping.html
deleted file mode 100644
index ca8ab9c35c49b..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/edit_role_mapping.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/index.tsx b/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/index.tsx
deleted file mode 100644
index b064a4dc50a22..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/index.tsx
+++ /dev/null
@@ -1,45 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-import React from 'react';
-import { render, unmountComponentAtNode } from 'react-dom';
-import routes from 'ui/routes';
-import { I18nContext } from 'ui/i18n';
-import { npSetup } from 'ui/new_platform';
-import { RoleMappingsAPI } from '../../../../lib/role_mappings_api';
-// @ts-ignore
-import template from './edit_role_mapping.html';
-import { CREATE_ROLE_MAPPING_PATH } from '../../management_urls';
-import { getEditRoleMappingBreadcrumbs } from '../../breadcrumbs';
-import { EditRoleMappingPage } from './components';
-
-routes.when(`${CREATE_ROLE_MAPPING_PATH}/:name?`, {
- template,
- k7Breadcrumbs: getEditRoleMappingBreadcrumbs,
- controller($scope, $route) {
- $scope.$$postDigest(() => {
- const domNode = document.getElementById('editRoleMappingReactRoot');
-
- const { name } = $route.current.params;
-
- render(
-
-
- ,
- domNode
- );
-
- // unmount react on controller destroy
- $scope.$on('$destroy', () => {
- if (domNode) {
- unmountComponentAtNode(domNode);
- }
- });
- });
- },
-});
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/role_mappings_grid_page.test.tsx b/x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/role_mappings_grid_page.test.tsx
deleted file mode 100644
index 259cdc71e25a2..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/role_mappings_grid_page.test.tsx
+++ /dev/null
@@ -1,182 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers';
-import { RoleMappingsGridPage } from '.';
-import { SectionLoading, PermissionDenied, NoCompatibleRealms } from '../../components';
-import { EmptyPrompt } from './empty_prompt';
-import { findTestSubject } from 'test_utils/find_test_subject';
-import { EuiLink } from '@elastic/eui';
-import { RoleMappingsAPI } from '../../../../../lib/role_mappings_api';
-import { act } from '@testing-library/react';
-
-describe('RoleMappingsGridPage', () => {
- it('renders an empty prompt when no role mappings exist', async () => {
- const roleMappingsAPI = ({
- getRoleMappings: jest.fn().mockResolvedValue([]),
- checkRoleMappingFeatures: jest.fn().mockResolvedValue({
- canManageRoleMappings: true,
- hasCompatibleRealms: true,
- }),
- } as unknown) as RoleMappingsAPI;
-
- const wrapper = mountWithIntl();
- expect(wrapper.find(SectionLoading)).toHaveLength(1);
- expect(wrapper.find(EmptyPrompt)).toHaveLength(0);
-
- await nextTick();
- wrapper.update();
-
- expect(wrapper.find(SectionLoading)).toHaveLength(0);
- expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0);
- expect(wrapper.find(EmptyPrompt)).toHaveLength(1);
- });
-
- it('renders a permission denied message when unauthorized to manage role mappings', async () => {
- const roleMappingsAPI = ({
- checkRoleMappingFeatures: jest.fn().mockResolvedValue({
- canManageRoleMappings: false,
- hasCompatibleRealms: true,
- }),
- } as unknown) as RoleMappingsAPI;
-
- const wrapper = mountWithIntl();
- expect(wrapper.find(SectionLoading)).toHaveLength(1);
- expect(wrapper.find(PermissionDenied)).toHaveLength(0);
-
- await nextTick();
- wrapper.update();
-
- expect(wrapper.find(SectionLoading)).toHaveLength(0);
- expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0);
- expect(wrapper.find(PermissionDenied)).toHaveLength(1);
- });
-
- it('renders a warning when there are no compatible realms enabled', async () => {
- const roleMappingsAPI = ({
- getRoleMappings: jest.fn().mockResolvedValue([
- {
- name: 'some realm',
- enabled: true,
- roles: [],
- rules: { field: { username: '*' } },
- },
- ]),
- checkRoleMappingFeatures: jest.fn().mockResolvedValue({
- canManageRoleMappings: true,
- hasCompatibleRealms: false,
- }),
- } as unknown) as RoleMappingsAPI;
-
- const wrapper = mountWithIntl();
- expect(wrapper.find(SectionLoading)).toHaveLength(1);
- expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0);
-
- await nextTick();
- wrapper.update();
-
- expect(wrapper.find(SectionLoading)).toHaveLength(0);
- expect(wrapper.find(NoCompatibleRealms)).toHaveLength(1);
- });
-
- it('renders links to mapped roles', async () => {
- const roleMappingsAPI = ({
- getRoleMappings: jest.fn().mockResolvedValue([
- {
- name: 'some realm',
- enabled: true,
- roles: ['superuser'],
- rules: { field: { username: '*' } },
- },
- ]),
- checkRoleMappingFeatures: jest.fn().mockResolvedValue({
- canManageRoleMappings: true,
- hasCompatibleRealms: true,
- }),
- } as unknown) as RoleMappingsAPI;
-
- const wrapper = mountWithIntl();
- await nextTick();
- wrapper.update();
-
- const links = findTestSubject(wrapper, 'roleMappingRoles').find(EuiLink);
- expect(links).toHaveLength(1);
- expect(links.at(0).props()).toMatchObject({
- href: '#/management/security/roles/edit/superuser',
- });
- });
-
- it('describes the number of mapped role templates', async () => {
- const roleMappingsAPI = ({
- getRoleMappings: jest.fn().mockResolvedValue([
- {
- name: 'some realm',
- enabled: true,
- role_templates: [{}, {}],
- rules: { field: { username: '*' } },
- },
- ]),
- checkRoleMappingFeatures: jest.fn().mockResolvedValue({
- canManageRoleMappings: true,
- hasCompatibleRealms: true,
- }),
- } as unknown) as RoleMappingsAPI;
-
- const wrapper = mountWithIntl();
- await nextTick();
- wrapper.update();
-
- const templates = findTestSubject(wrapper, 'roleMappingRoles');
- expect(templates).toHaveLength(1);
- expect(templates.text()).toEqual(`2 role templates defined`);
- });
-
- it('allows role mappings to be deleted, refreshing the grid after', async () => {
- const roleMappingsAPI = ({
- getRoleMappings: jest.fn().mockResolvedValue([
- {
- name: 'some-realm',
- enabled: true,
- roles: ['superuser'],
- rules: { field: { username: '*' } },
- },
- ]),
- checkRoleMappingFeatures: jest.fn().mockResolvedValue({
- canManageRoleMappings: true,
- hasCompatibleRealms: true,
- }),
- deleteRoleMappings: jest.fn().mockReturnValue(
- Promise.resolve([
- {
- name: 'some-realm',
- success: true,
- },
- ])
- ),
- } as unknown) as RoleMappingsAPI;
-
- const wrapper = mountWithIntl();
- await nextTick();
- wrapper.update();
-
- expect(roleMappingsAPI.getRoleMappings).toHaveBeenCalledTimes(1);
- expect(roleMappingsAPI.deleteRoleMappings).not.toHaveBeenCalled();
-
- findTestSubject(wrapper, `deleteRoleMappingButton-some-realm`).simulate('click');
- expect(findTestSubject(wrapper, 'deleteRoleMappingConfirmationModal')).toHaveLength(1);
-
- await act(async () => {
- findTestSubject(wrapper, 'confirmModalConfirmButton').simulate('click');
- await nextTick();
- wrapper.update();
- });
-
- expect(roleMappingsAPI.deleteRoleMappings).toHaveBeenCalledWith(['some-realm']);
- // Expect an additional API call to refresh the grid
- expect(roleMappingsAPI.getRoleMappings).toHaveBeenCalledTimes(2);
- });
-});
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/index.tsx b/x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/index.tsx
deleted file mode 100644
index 9e925d0fa6dc0..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/index.tsx
+++ /dev/null
@@ -1,40 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-import React from 'react';
-import { render, unmountComponentAtNode } from 'react-dom';
-import routes from 'ui/routes';
-import { I18nContext } from 'ui/i18n';
-import { npSetup } from 'ui/new_platform';
-import { RoleMappingsAPI } from '../../../../lib/role_mappings_api';
-// @ts-ignore
-import template from './role_mappings.html';
-import { ROLE_MAPPINGS_PATH } from '../../management_urls';
-import { getRoleMappingBreadcrumbs } from '../../breadcrumbs';
-import { RoleMappingsGridPage } from './components';
-
-routes.when(ROLE_MAPPINGS_PATH, {
- template,
- k7Breadcrumbs: getRoleMappingBreadcrumbs,
- controller($scope) {
- $scope.$$postDigest(() => {
- const domNode = document.getElementById('roleMappingsGridReactRoot');
-
- render(
-
-
- ,
- domNode
- );
-
- // unmount react on controller destroy
- $scope.$on('$destroy', () => {
- if (domNode) {
- unmountComponentAtNode(domNode);
- }
- });
- });
- },
-});
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/role_mappings.html b/x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/role_mappings.html
deleted file mode 100644
index cff3b821d132c..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/role_mappings.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/x-pack/legacy/plugins/security/public/views/management/roles_grid/roles.html b/x-pack/legacy/plugins/security/public/views/management/roles_grid/roles.html
deleted file mode 100644
index 0552b655afafd..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/roles_grid/roles.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/x-pack/legacy/plugins/security/public/views/management/roles_grid/roles.js b/x-pack/legacy/plugins/security/public/views/management/roles_grid/roles.js
deleted file mode 100644
index e9c42824711b3..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/roles_grid/roles.js
+++ /dev/null
@@ -1,35 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-import React from 'react';
-import { render, unmountComponentAtNode } from 'react-dom';
-import routes from 'ui/routes';
-import template from 'plugins/security/views/management/roles_grid/roles.html';
-import { ROLES_PATH } from '../management_urls';
-import { getRolesBreadcrumbs } from '../breadcrumbs';
-import { I18nContext } from 'ui/i18n';
-import { RolesGridPage } from './components';
-
-routes.when(ROLES_PATH, {
- template,
- k7Breadcrumbs: getRolesBreadcrumbs,
- controller($scope) {
- $scope.$$postDigest(() => {
- const domNode = document.getElementById('rolesGridReactRoot');
-
- render(
-
-
- ,
- domNode
- );
-
- // unmount react on controller destroy
- $scope.$on('$destroy', () => {
- unmountComponentAtNode(domNode);
- });
- });
- },
-});
diff --git a/x-pack/legacy/plugins/security/public/views/management/users_grid/users.html b/x-pack/legacy/plugins/security/public/views/management/users_grid/users.html
deleted file mode 100644
index 3dce7326d001a..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/users_grid/users.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/x-pack/legacy/plugins/security/public/views/management/users_grid/users.js b/x-pack/legacy/plugins/security/public/views/management/users_grid/users.js
deleted file mode 100644
index 8d4e0526251d7..0000000000000
--- a/x-pack/legacy/plugins/security/public/views/management/users_grid/users.js
+++ /dev/null
@@ -1,47 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { render, unmountComponentAtNode } from 'react-dom';
-import routes from 'ui/routes';
-import template from 'plugins/security/views/management/users_grid/users.html';
-import { SECURITY_PATH, USERS_PATH } from '../management_urls';
-import { UsersListPage } from './components';
-import { UserAPIClient } from '../../../lib/api';
-import { I18nContext } from 'ui/i18n';
-import { getUsersBreadcrumbs } from '../breadcrumbs';
-
-routes.when(SECURITY_PATH, {
- redirectTo: USERS_PATH,
-});
-
-const renderReact = (elem, changeUrl) => {
- render(
-
-
- ,
- elem
- );
-};
-
-routes.when(USERS_PATH, {
- template,
- k7Breadcrumbs: getUsersBreadcrumbs,
- controller($scope, $http, kbnUrl) {
- $scope.$on('$destroy', () => {
- const elem = document.getElementById('usersReactRoot');
- if (elem) unmountComponentAtNode(elem);
- });
- $scope.$$postDigest(() => {
- const elem = document.getElementById('usersReactRoot');
- const changeUrl = url => {
- kbnUrl.change(url);
- $scope.$apply();
- };
- renderReact(elem, $http, changeUrl);
- });
- },
-});
diff --git a/x-pack/legacy/plugins/security/public/views/overwritten_session/overwritten_session.tsx b/x-pack/legacy/plugins/security/public/views/overwritten_session/overwritten_session.tsx
index fb39c517e1c2c..4c79c499cc0e6 100644
--- a/x-pack/legacy/plugins/security/public/views/overwritten_session/overwritten_session.tsx
+++ b/x-pack/legacy/plugins/security/public/views/overwritten_session/overwritten_session.tsx
@@ -11,8 +11,7 @@ import { render } from 'react-dom';
import chrome from 'ui/chrome';
import { I18nContext } from 'ui/i18n';
import { npSetup } from 'ui/new_platform';
-import { SecurityPluginSetup } from '../../../../../../plugins/security/public';
-import { AuthenticatedUser } from '../../../common/model';
+import { AuthenticatedUser, SecurityPluginSetup } from '../../../../../../plugins/security/public';
import { AuthenticationStatePage } from '../../components/authentication_state_page';
chrome
diff --git a/x-pack/plugins/security/common/model/index.ts b/x-pack/plugins/security/common/model/index.ts
index f3c65ed7e3cf1..121791d113bd5 100644
--- a/x-pack/plugins/security/common/model/index.ts
+++ b/x-pack/plugins/security/common/model/index.ts
@@ -10,7 +10,16 @@ export { AuthenticatedUser, canUserChangePassword } from './authenticated_user';
export { BuiltinESPrivileges } from './builtin_es_privileges';
export { FeaturesPrivileges } from './features_privileges';
export { RawKibanaPrivileges, RawKibanaFeaturePrivileges } from './raw_kibana_privileges';
-export { Role, RoleIndexPrivilege, RoleKibanaPrivilege } from './role';
+export {
+ Role,
+ RoleIndexPrivilege,
+ RoleKibanaPrivilege,
+ copyRole,
+ isReadOnlyRole,
+ isReservedRole,
+ isRoleEnabled,
+ prepareRoleClone,
+} from './role';
export { KibanaPrivileges } from './kibana_privileges';
export {
InlineRoleTemplate,
diff --git a/x-pack/legacy/plugins/security/public/lib/role_utils.test.ts b/x-pack/plugins/security/common/model/role.test.ts
similarity index 96%
rename from x-pack/legacy/plugins/security/public/lib/role_utils.test.ts
rename to x-pack/plugins/security/common/model/role.test.ts
index 9d94017c3f0fe..d4a910a1785eb 100644
--- a/x-pack/legacy/plugins/security/public/lib/role_utils.test.ts
+++ b/x-pack/plugins/security/common/model/role.test.ts
@@ -4,14 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { Role } from '../../common/model';
-import {
- copyRole,
- isReadOnlyRole,
- isReservedRole,
- isRoleEnabled,
- prepareRoleClone,
-} from './role_utils';
+import { Role, isReadOnlyRole, isReservedRole, isRoleEnabled, copyRole, prepareRoleClone } from '.';
describe('role', () => {
describe('isRoleEnabled', () => {
diff --git a/x-pack/plugins/security/common/model/role.ts b/x-pack/plugins/security/common/model/role.ts
index 89f68aaa55b5c..1edcf147262ed 100644
--- a/x-pack/plugins/security/common/model/role.ts
+++ b/x-pack/plugins/security/common/model/role.ts
@@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { cloneDeep } from 'lodash';
import { FeaturesPrivileges } from './features_privileges';
export interface RoleIndexPrivilege {
@@ -40,3 +41,53 @@ export interface Role {
_transform_error?: string[];
_unrecognized_applications?: string[];
}
+
+/**
+ * Returns whether given role is enabled or not
+ *
+ * @param role Object Role JSON, as returned by roles API
+ * @return Boolean true if role is enabled; false otherwise
+ */
+export function isRoleEnabled(role: Partial) {
+ return role.transient_metadata?.enabled ?? true;
+}
+
+/**
+ * Returns whether given role is reserved or not.
+ *
+ * @param role Role as returned by roles API
+ */
+export function isReservedRole(role: Partial) {
+ return (role.metadata?._reserved as boolean) ?? false;
+}
+
+/**
+ * Returns whether given role is editable through the UI or not.
+ *
+ * @param role the Role as returned by roles API
+ */
+export function isReadOnlyRole(role: Partial): boolean {
+ return isReservedRole(role) || (role._transform_error?.length ?? 0) > 0;
+}
+
+/**
+ * Returns a deep copy of the role.
+ *
+ * @param role the Role to copy.
+ */
+export function copyRole(role: Role) {
+ return cloneDeep(role);
+}
+
+/**
+ * Creates a deep copy of the role suitable for cloning.
+ *
+ * @param role the Role to clone.
+ */
+export function prepareRoleClone(role: Role): Role {
+ const clone = copyRole(role);
+
+ clone.name = '';
+
+ return clone;
+}
diff --git a/x-pack/plugins/security/kibana.json b/x-pack/plugins/security/kibana.json
index 32f860b1423d3..7d1940e393bec 100644
--- a/x-pack/plugins/security/kibana.json
+++ b/x-pack/plugins/security/kibana.json
@@ -3,7 +3,8 @@
"version": "8.0.0",
"kibanaVersion": "kibana",
"configPath": ["xpack", "security"],
- "requiredPlugins": ["features", "licensing"],
+ "requiredPlugins": ["data", "features", "licensing"],
+ "optionalPlugins": ["home", "management"],
"server": true,
"ui": true
}
diff --git a/x-pack/plugins/security/public/_index.scss b/x-pack/plugins/security/public/_index.scss
new file mode 100644
index 0000000000000..9fa81bad7c3f4
--- /dev/null
+++ b/x-pack/plugins/security/public/_index.scss
@@ -0,0 +1,2 @@
+// Management styles
+@import './management/index';
diff --git a/x-pack/legacy/plugins/security/public/views/account/components/account_management_page.test.tsx b/x-pack/plugins/security/public/account_management/account_management_page.test.tsx
similarity index 72%
rename from x-pack/legacy/plugins/security/public/views/account/components/account_management_page.test.tsx
rename to x-pack/plugins/security/public/account_management/account_management_page.test.tsx
index 366842e58e9e4..b7cf8e6dd1418 100644
--- a/x-pack/legacy/plugins/security/public/views/account/components/account_management_page.test.tsx
+++ b/x-pack/plugins/security/public/account_management/account_management_page.test.tsx
@@ -6,11 +6,12 @@
import React from 'react';
import { act } from '@testing-library/react';
import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers';
-import { securityMock } from '../../../../../../../plugins/security/public/mocks';
+import { AuthenticatedUser } from '../../common/model';
import { AccountManagementPage } from './account_management_page';
-import { AuthenticatedUser } from '../../../../common/model';
-jest.mock('ui/kfetch');
+import { coreMock } from 'src/core/public/mocks';
+import { securityMock } from '../mocks';
+import { userAPIClientMock } from '../management/users/index.mock';
interface Options {
withFullName?: boolean;
@@ -45,7 +46,11 @@ describe('', () => {
it(`displays users full name, username, and email address`, async () => {
const user = createUser();
const wrapper = mountWithIntl(
-
+
);
await act(async () => {
@@ -63,7 +68,11 @@ describe('', () => {
it(`displays username when full_name is not provided`, async () => {
const user = createUser({ withFullName: false });
const wrapper = mountWithIntl(
-
+
);
await act(async () => {
@@ -77,7 +86,11 @@ describe('', () => {
it(`displays a placeholder when no email address is provided`, async () => {
const user = createUser({ withEmail: false });
const wrapper = mountWithIntl(
-
+
);
await act(async () => {
@@ -91,7 +104,11 @@ describe('', () => {
it(`displays change password form for users in the native realm`, async () => {
const user = createUser();
const wrapper = mountWithIntl(
-
+
);
await act(async () => {
@@ -106,7 +123,11 @@ describe('', () => {
it(`does not display change password form for users in the saml realm`, async () => {
const user = createUser({ realm: 'saml' });
const wrapper = mountWithIntl(
-
+
);
await act(async () => {
diff --git a/x-pack/legacy/plugins/security/public/views/account/components/account_management_page.tsx b/x-pack/plugins/security/public/account_management/account_management_page.tsx
similarity index 62%
rename from x-pack/legacy/plugins/security/public/views/account/components/account_management_page.tsx
rename to x-pack/plugins/security/public/account_management/account_management_page.tsx
index 6abee73e0b353..3f764adc7949f 100644
--- a/x-pack/legacy/plugins/security/public/views/account/components/account_management_page.tsx
+++ b/x-pack/plugins/security/public/account_management/account_management_page.tsx
@@ -5,20 +5,24 @@
*/
import { EuiPage, EuiPageBody, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui';
import React, { useEffect, useState } from 'react';
-import { SecurityPluginSetup } from '../../../../../../../plugins/security/public';
-import { getUserDisplayName, AuthenticatedUser } from '../../../../common/model';
+import { NotificationsStart } from 'src/core/public';
+import { getUserDisplayName, AuthenticatedUser } from '../../common/model';
+import { AuthenticationServiceSetup } from '../authentication';
import { ChangePassword } from './change_password';
+import { UserAPIClient } from '../management';
import { PersonalInfo } from './personal_info';
interface Props {
- securitySetup: SecurityPluginSetup;
+ authc: AuthenticationServiceSetup;
+ apiClient: PublicMethodsOf;
+ notifications: NotificationsStart;
}
-export const AccountManagementPage = (props: Props) => {
+export const AccountManagementPage = ({ apiClient, authc, notifications }: Props) => {
const [currentUser, setCurrentUser] = useState(null);
useEffect(() => {
- props.securitySetup.authc.getCurrentUser().then(setCurrentUser);
- }, [props]);
+ authc.getCurrentUser().then(setCurrentUser);
+ }, [authc]);
if (!currentUser) {
return null;
@@ -36,7 +40,7 @@ export const AccountManagementPage = (props: Props) => {
-
+
diff --git a/x-pack/legacy/plugins/security/public/views/account/components/change_password/change_password.tsx b/x-pack/plugins/security/public/account_management/change_password/change_password.tsx
similarity index 81%
rename from x-pack/legacy/plugins/security/public/views/account/components/change_password/change_password.tsx
rename to x-pack/plugins/security/public/account_management/change_password/change_password.tsx
index 63abb4539470d..f5ac5f3b21d2e 100644
--- a/x-pack/legacy/plugins/security/public/views/account/components/change_password/change_password.tsx
+++ b/x-pack/plugins/security/public/account_management/change_password/change_password.tsx
@@ -3,18 +3,18 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- // @ts-ignore
- EuiDescribedFormGroup,
-} from '@elastic/eui';
-import { FormattedMessage } from '@kbn/i18n/react';
import React, { Component } from 'react';
-import { UserAPIClient } from '../../../../lib/api';
-import { AuthenticatedUser, canUserChangePassword } from '../../../../../common/model';
-import { ChangePasswordForm } from '../../../../components/management/change_password_form';
+import { EuiDescribedFormGroup } from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { NotificationsSetup } from 'src/core/public';
+import { AuthenticatedUser, canUserChangePassword } from '../../../common/model';
+import { UserAPIClient } from '../../management/users';
+import { ChangePasswordForm } from '../../management/users/components/change_password_form';
interface Props {
user: AuthenticatedUser;
+ apiClient: PublicMethodsOf;
+ notifications: NotificationsSetup;
}
export class ChangePassword extends Component {
@@ -48,7 +48,8 @@ export class ChangePassword extends Component {
);
diff --git a/x-pack/legacy/plugins/security/public/views/account/components/change_password/index.ts b/x-pack/plugins/security/public/account_management/change_password/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/account/components/change_password/index.ts
rename to x-pack/plugins/security/public/account_management/change_password/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/account/components/index.ts b/x-pack/plugins/security/public/account_management/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/account/components/index.ts
rename to x-pack/plugins/security/public/account_management/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/account/components/personal_info/index.ts b/x-pack/plugins/security/public/account_management/personal_info/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/account/components/personal_info/index.ts
rename to x-pack/plugins/security/public/account_management/personal_info/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/account/components/personal_info/personal_info.tsx b/x-pack/plugins/security/public/account_management/personal_info/personal_info.tsx
similarity index 89%
rename from x-pack/legacy/plugins/security/public/views/account/components/personal_info/personal_info.tsx
rename to x-pack/plugins/security/public/account_management/personal_info/personal_info.tsx
index 7121bf7ab28ee..9cbbc242e8400 100644
--- a/x-pack/legacy/plugins/security/public/views/account/components/personal_info/personal_info.tsx
+++ b/x-pack/plugins/security/public/account_management/personal_info/personal_info.tsx
@@ -3,15 +3,10 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- // @ts-ignore
- EuiDescribedFormGroup,
- EuiFormRow,
- EuiText,
-} from '@elastic/eui';
-import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
-import { AuthenticatedUser } from '../../../../../common/model';
+import { EuiDescribedFormGroup, EuiFormRow, EuiText } from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { AuthenticatedUser } from '../../../common/model';
interface Props {
user: AuthenticatedUser;
diff --git a/x-pack/plugins/security/public/management/_index.scss b/x-pack/plugins/security/public/management/_index.scss
new file mode 100644
index 0000000000000..5d419b5323079
--- /dev/null
+++ b/x-pack/plugins/security/public/management/_index.scss
@@ -0,0 +1,3 @@
+@import './roles/index';
+@import './users/index';
+@import './role_mappings/index';
diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.mock.ts b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.mock.ts
new file mode 100644
index 0000000000000..2a45d497029f4
--- /dev/null
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.mock.ts
@@ -0,0 +1,13 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export const apiKeysAPIClientMock = {
+ create: () => ({
+ checkPrivileges: jest.fn(),
+ getApiKeys: jest.fn(),
+ invalidateApiKeys: jest.fn(),
+ }),
+};
diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.test.ts b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.test.ts
new file mode 100644
index 0000000000000..7d51a80459a6e
--- /dev/null
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.test.ts
@@ -0,0 +1,86 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { APIKeysAPIClient } from './api_keys_api_client';
+
+import { httpServiceMock } from '../../../../../../src/core/public/mocks';
+
+describe('APIKeysAPIClient', () => {
+ it('checkPrivileges() queries correct endpoint', async () => {
+ const httpMock = httpServiceMock.createStartContract();
+
+ const mockResponse = Symbol('mockResponse');
+ httpMock.get.mockResolvedValue(mockResponse);
+
+ const apiClient = new APIKeysAPIClient(httpMock);
+
+ await expect(apiClient.checkPrivileges()).resolves.toBe(mockResponse);
+ expect(httpMock.get).toHaveBeenCalledTimes(1);
+ expect(httpMock.get).toHaveBeenCalledWith('/internal/security/api_key/privileges');
+ });
+
+ it('getApiKeys() queries correct endpoint', async () => {
+ const httpMock = httpServiceMock.createStartContract();
+
+ const mockResponse = Symbol('mockResponse');
+ httpMock.get.mockResolvedValue(mockResponse);
+
+ const apiClient = new APIKeysAPIClient(httpMock);
+
+ await expect(apiClient.getApiKeys()).resolves.toBe(mockResponse);
+ expect(httpMock.get).toHaveBeenCalledTimes(1);
+ expect(httpMock.get).toHaveBeenCalledWith('/internal/security/api_key', {
+ query: { isAdmin: false },
+ });
+ httpMock.get.mockClear();
+
+ await expect(apiClient.getApiKeys(false)).resolves.toBe(mockResponse);
+ expect(httpMock.get).toHaveBeenCalledTimes(1);
+ expect(httpMock.get).toHaveBeenCalledWith('/internal/security/api_key', {
+ query: { isAdmin: false },
+ });
+ httpMock.get.mockClear();
+
+ await expect(apiClient.getApiKeys(true)).resolves.toBe(mockResponse);
+ expect(httpMock.get).toHaveBeenCalledTimes(1);
+ expect(httpMock.get).toHaveBeenCalledWith('/internal/security/api_key', {
+ query: { isAdmin: true },
+ });
+ });
+
+ it('invalidateApiKeys() queries correct endpoint', async () => {
+ const httpMock = httpServiceMock.createStartContract();
+
+ const mockResponse = Symbol('mockResponse');
+ httpMock.post.mockResolvedValue(mockResponse);
+
+ const apiClient = new APIKeysAPIClient(httpMock);
+ const mockAPIKeys = [
+ { id: 'one', name: 'name-one' },
+ { id: 'two', name: 'name-two' },
+ ];
+
+ await expect(apiClient.invalidateApiKeys(mockAPIKeys)).resolves.toBe(mockResponse);
+ expect(httpMock.post).toHaveBeenCalledTimes(1);
+ expect(httpMock.post).toHaveBeenCalledWith('/internal/security/api_key/invalidate', {
+ body: JSON.stringify({ apiKeys: mockAPIKeys, isAdmin: false }),
+ });
+ httpMock.post.mockClear();
+
+ await expect(apiClient.invalidateApiKeys(mockAPIKeys, false)).resolves.toBe(mockResponse);
+ expect(httpMock.post).toHaveBeenCalledTimes(1);
+ expect(httpMock.post).toHaveBeenCalledWith('/internal/security/api_key/invalidate', {
+ body: JSON.stringify({ apiKeys: mockAPIKeys, isAdmin: false }),
+ });
+ httpMock.post.mockClear();
+
+ await expect(apiClient.invalidateApiKeys(mockAPIKeys, true)).resolves.toBe(mockResponse);
+ expect(httpMock.post).toHaveBeenCalledTimes(1);
+ expect(httpMock.post).toHaveBeenCalledWith('/internal/security/api_key/invalidate', {
+ body: JSON.stringify({ apiKeys: mockAPIKeys, isAdmin: true }),
+ });
+ });
+});
diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts
new file mode 100644
index 0000000000000..372b1e56a73c4
--- /dev/null
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts
@@ -0,0 +1,42 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { HttpStart } from 'src/core/public';
+import { ApiKey, ApiKeyToInvalidate } from '../../../common/model';
+
+interface CheckPrivilegesResponse {
+ areApiKeysEnabled: boolean;
+ isAdmin: boolean;
+}
+
+interface InvalidateApiKeysResponse {
+ itemsInvalidated: ApiKeyToInvalidate[];
+ errors: any[];
+}
+
+interface GetApiKeysResponse {
+ apiKeys: ApiKey[];
+}
+
+const apiKeysUrl = '/internal/security/api_key';
+
+export class APIKeysAPIClient {
+ constructor(private readonly http: HttpStart) {}
+
+ public async checkPrivileges() {
+ return await this.http.get(`${apiKeysUrl}/privileges`);
+ }
+
+ public async getApiKeys(isAdmin = false) {
+ return await this.http.get(apiKeysUrl, { query: { isAdmin } });
+ }
+
+ public async invalidateApiKeys(apiKeys: ApiKeyToInvalidate[], isAdmin = false) {
+ return await this.http.post(`${apiKeysUrl}/invalidate`, {
+ body: JSON.stringify({ apiKeys, isAdmin }),
+ });
+ }
+}
diff --git a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/__snapshots__/api_keys_grid_page.test.tsx.snap b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/__snapshots__/api_keys_grid_page.test.tsx.snap
similarity index 91%
rename from x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/__snapshots__/api_keys_grid_page.test.tsx.snap
rename to x-pack/plugins/security/public/management/api_keys/api_keys_grid/__snapshots__/api_keys_grid_page.test.tsx.snap
index c2537235c99f6..42fd4417e238b 100644
--- a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/__snapshots__/api_keys_grid_page.test.tsx.snap
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/__snapshots__/api_keys_grid_page.test.tsx.snap
@@ -1,7 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`ApiKeysGridPage renders a callout when API keys are not enabled 1`] = `
-
+exports[`APIKeysGridPage renders a callout when API keys are not enabled 1`] = `
+
Contact your system administrator and refer to the
`;
-exports[`ApiKeysGridPage renders permission denied if user does not have required permissions 1`] = `
+exports[`APIKeysGridPage renders permission denied if user does not have required permissions 1`] = `
({ body: { statusCode: 403 } });
-const mock500 = () => ({ body: { error: 'Internal Server Error', message: '', statusCode: 500 } });
-
-jest.mock('../../../../lib/api_keys_api', () => {
- return {
- ApiKeysApi: {
- async checkPrivileges() {
- if (mockSimulate403) {
- throw mock403();
- }
-
- return {
- isAdmin: mockIsAdmin,
- areApiKeysEnabled: mockAreApiKeysEnabled,
- };
- },
- async getApiKeys() {
- if (mockSimulate500) {
- throw mock500();
- }
-
- return {
- apiKeys: [
- {
- creation: 1571322182082,
- expiration: 1571408582082,
- id: '0QQZ2m0BO2XZwgJFuWTT',
- invalidated: false,
- name: 'my-api-key',
- realm: 'reserved',
- username: 'elastic',
- },
- ],
- };
- },
- },
- };
-});
-
import { mountWithIntl } from 'test_utils/enzyme_helpers';
-import { ApiKeysGridPage } from './api_keys_grid_page';
import React from 'react';
import { ReactWrapper } from 'enzyme';
import { EuiCallOut } from '@elastic/eui';
import { NotEnabled } from './not_enabled';
import { PermissionDenied } from './permission_denied';
+import { APIKeysAPIClient } from '../api_keys_api_client';
+import { DocumentationLinksService } from '../documentation_links';
+import { APIKeysGridPage } from './api_keys_grid_page';
+
+import { coreMock } from '../../../../../../../src/core/public/mocks';
+import { apiKeysAPIClientMock } from '../index.mock';
+
+const mock403 = () => ({ body: { statusCode: 403 } });
+const mock500 = () => ({ body: { error: 'Internal Server Error', message: '', statusCode: 500 } });
const waitForRender = async (
wrapper: ReactWrapper,
@@ -77,23 +41,51 @@ const waitForRender = async (
});
};
-describe('ApiKeysGridPage', () => {
+describe('APIKeysGridPage', () => {
+ let apiClientMock: jest.Mocked>;
beforeEach(() => {
- mockSimulate403 = false;
- mockSimulate500 = false;
- mockAreApiKeysEnabled = true;
- mockIsAdmin = true;
+ apiClientMock = apiKeysAPIClientMock.create();
+ apiClientMock.checkPrivileges.mockResolvedValue({
+ isAdmin: true,
+ areApiKeysEnabled: true,
+ });
+ apiClientMock.getApiKeys.mockResolvedValue({
+ apiKeys: [
+ {
+ creation: 1571322182082,
+ expiration: 1571408582082,
+ id: '0QQZ2m0BO2XZwgJFuWTT',
+ invalidated: false,
+ name: 'my-api-key',
+ realm: 'reserved',
+ username: 'elastic',
+ },
+ ],
+ });
});
+ const getViewProperties = () => {
+ const { docLinks, notifications } = coreMock.createStart();
+ return {
+ docLinks: new DocumentationLinksService(docLinks),
+ notifications,
+ apiKeysAPIClient: apiClientMock,
+ };
+ };
+
it('renders a loading state when fetching API keys', async () => {
- const wrapper = mountWithIntl();
+ const wrapper = mountWithIntl();
expect(wrapper.find('[data-test-subj="apiKeysSectionLoading"]')).toHaveLength(1);
});
it('renders a callout when API keys are not enabled', async () => {
- mockAreApiKeysEnabled = false;
- const wrapper = mountWithIntl();
+ apiClientMock.checkPrivileges.mockResolvedValue({
+ isAdmin: true,
+ areApiKeysEnabled: false,
+ });
+
+ const wrapper = mountWithIntl();
await waitForRender(wrapper, updatedWrapper => {
return updatedWrapper.find(NotEnabled).length > 0;
@@ -103,8 +95,9 @@ describe('ApiKeysGridPage', () => {
});
it('renders permission denied if user does not have required permissions', async () => {
- mockSimulate403 = true;
- const wrapper = mountWithIntl();
+ apiClientMock.checkPrivileges.mockRejectedValue(mock403());
+
+ const wrapper = mountWithIntl();
await waitForRender(wrapper, updatedWrapper => {
return updatedWrapper.find(PermissionDenied).length > 0;
@@ -114,8 +107,9 @@ describe('ApiKeysGridPage', () => {
});
it('renders error callout if error fetching API keys', async () => {
- mockSimulate500 = true;
- const wrapper = mountWithIntl();
+ apiClientMock.getApiKeys.mockRejectedValue(mock500());
+
+ const wrapper = mountWithIntl();
await waitForRender(wrapper, updatedWrapper => {
return updatedWrapper.find(EuiCallOut).length > 0;
@@ -125,7 +119,10 @@ describe('ApiKeysGridPage', () => {
});
describe('Admin view', () => {
- const wrapper = mountWithIntl();
+ let wrapper: ReactWrapper;
+ beforeEach(() => {
+ wrapper = mountWithIntl();
+ });
it('renders a callout indicating the user is an administrator', async () => {
const calloutEl = 'EuiCallOut[data-test-subj="apiKeyAdminDescriptionCallOut"]';
@@ -151,8 +148,15 @@ describe('ApiKeysGridPage', () => {
});
describe('Non-admin view', () => {
- mockIsAdmin = false;
- const wrapper = mountWithIntl();
+ let wrapper: ReactWrapper;
+ beforeEach(() => {
+ apiClientMock.checkPrivileges.mockResolvedValue({
+ isAdmin: false,
+ areApiKeysEnabled: true,
+ });
+
+ wrapper = mountWithIntl();
+ });
it('does NOT render a callout indicating the user is an administrator', async () => {
const descriptionEl = 'EuiText[data-test-subj="apiKeysDescriptionText"]';
diff --git a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/api_keys_grid_page.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx
similarity index 90%
rename from x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/api_keys_grid_page.tsx
rename to x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx
index 92633a4b0ef57..779a2302cfadf 100644
--- a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/api_keys_grid_page.tsx
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx
@@ -27,16 +27,23 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import moment from 'moment-timezone';
import _ from 'lodash';
-import { toastNotifications } from 'ui/notify';
+import { NotificationsStart } from 'src/core/public';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { SectionLoading } from '../../../../../../../../../src/plugins/es_ui_shared/public/components/section_loading';
-import { ApiKey, ApiKeyToInvalidate } from '../../../../../common/model';
-import { ApiKeysApi } from '../../../../lib/api_keys_api';
+import { SectionLoading } from '../../../../../../../src/plugins/es_ui_shared/public/components/section_loading';
+import { ApiKey, ApiKeyToInvalidate } from '../../../../common/model';
+import { APIKeysAPIClient } from '../api_keys_api_client';
+import { DocumentationLinksService } from '../documentation_links';
import { PermissionDenied } from './permission_denied';
import { EmptyPrompt } from './empty_prompt';
import { NotEnabled } from './not_enabled';
import { InvalidateProvider } from './invalidate_provider';
+interface Props {
+ notifications: NotificationsStart;
+ docLinks: DocumentationLinksService;
+ apiKeysAPIClient: PublicMethodsOf;
+}
+
interface State {
isLoadingApp: boolean;
isLoadingTable: boolean;
@@ -50,7 +57,7 @@ interface State {
const DATE_FORMAT = 'MMMM Do YYYY HH:mm:ss';
-export class ApiKeysGridPage extends Component {
+export class APIKeysGridPage extends Component {
constructor(props: any) {
super(props);
this.state = {
@@ -124,7 +131,7 @@ export class ApiKeysGridPage extends Component {
if (!areApiKeysEnabled) {
return (
-
+
);
}
@@ -132,7 +139,7 @@ export class ApiKeysGridPage extends Component {
if (!isLoadingTable && apiKeys && apiKeys.length === 0) {
return (
-
+
);
}
@@ -210,7 +217,11 @@ export class ApiKeysGridPage extends Component {
const search: EuiInMemoryTableProps['search'] = {
toolsLeft: selectedItems.length ? (
-
+
{invalidateApiKeyPrompt => {
return (
{
return (
-
+
{invalidateApiKeyPrompt => {
return (
{
private async checkPrivileges() {
try {
- const { isAdmin, areApiKeysEnabled } = await ApiKeysApi.checkPrivileges();
+ const { isAdmin, areApiKeysEnabled } = await this.props.apiKeysAPIClient.checkPrivileges();
this.setState({ isAdmin, areApiKeysEnabled });
if (areApiKeysEnabled) {
@@ -494,14 +509,11 @@ export class ApiKeysGridPage extends Component {
if (_.get(e, 'body.statusCode') === 403) {
this.setState({ permissionDenied: true, isLoadingApp: false });
} else {
- toastNotifications.addDanger(
- this.props.i18n.translate(
- 'xpack.security.management.apiKeys.table.fetchingApiKeysErrorMessage',
- {
- defaultMessage: 'Error checking privileges: {message}',
- values: { message: _.get(e, 'body.message', '') },
- }
- )
+ this.props.notifications.toasts.addDanger(
+ i18n.translate('xpack.security.management.apiKeys.table.fetchingApiKeysErrorMessage', {
+ defaultMessage: 'Error checking privileges: {message}',
+ values: { message: _.get(e, 'body.message', '') },
+ })
);
}
}
@@ -520,7 +532,7 @@ export class ApiKeysGridPage extends Component {
private loadApiKeys = async () => {
try {
const { isAdmin } = this.state;
- const { apiKeys } = await ApiKeysApi.getApiKeys(isAdmin);
+ const { apiKeys } = await this.props.apiKeysAPIClient.getApiKeys(isAdmin);
this.setState({ apiKeys });
} catch (e) {
this.setState({ error: e });
diff --git a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/empty_prompt/empty_prompt.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/empty_prompt/empty_prompt.tsx
similarity index 89%
rename from x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/empty_prompt/empty_prompt.tsx
rename to x-pack/plugins/security/public/management/api_keys/api_keys_grid/empty_prompt/empty_prompt.tsx
index 957ca7010a1a0..7d762a1ceb04e 100644
--- a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/empty_prompt/empty_prompt.tsx
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/empty_prompt/empty_prompt.tsx
@@ -7,13 +7,14 @@
import React, { Fragment } from 'react';
import { EuiEmptyPrompt, EuiButton, EuiLink } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
-import { documentationLinks } from '../../services/documentation_links';
+import { DocumentationLinksService } from '../../documentation_links';
interface Props {
isAdmin: boolean;
+ docLinks: DocumentationLinksService;
}
-export const EmptyPrompt: React.FunctionComponent = ({ isAdmin }) => (
+export const EmptyPrompt: React.FunctionComponent = ({ isAdmin, docLinks }) => (
= ({ isAdmin }) => (
defaultMessage="You can create an {link} from Console."
values={{
link: (
-
+
React.ReactElement;
+ notifications: NotificationsStart;
+ apiKeysAPIClient: PublicMethodsOf;
}
export type InvalidateApiKeys = (
@@ -23,7 +25,12 @@ export type InvalidateApiKeys = (
type OnSuccessCallback = (apiKeysInvalidated: ApiKeyToInvalidate[]) => void;
-export const InvalidateProvider: React.FunctionComponent = ({ isAdmin, children }) => {
+export const InvalidateProvider: React.FunctionComponent = ({
+ isAdmin,
+ children,
+ notifications,
+ apiKeysAPIClient,
+}) => {
const [apiKeys, setApiKeys] = useState([]);
const [isModalOpen, setIsModalOpen] = useState(false);
const onSuccessCallback = useRef(null);
@@ -48,7 +55,7 @@ export const InvalidateProvider: React.FunctionComponent = ({ isAdmin, ch
let errors;
try {
- result = await ApiKeysApi.invalidateApiKeys(apiKeys, isAdmin);
+ result = await apiKeysAPIClient.invalidateApiKeys(apiKeys, isAdmin);
} catch (e) {
error = e;
}
@@ -77,7 +84,7 @@ export const InvalidateProvider: React.FunctionComponent = ({ isAdmin, ch
values: { name: itemsInvalidated[0].name },
}
);
- toastNotifications.addSuccess(successMessage);
+ notifications.toasts.addSuccess(successMessage);
if (onSuccessCallback.current) {
onSuccessCallback.current([...itemsInvalidated]);
}
@@ -106,7 +113,7 @@ export const InvalidateProvider: React.FunctionComponent = ({ isAdmin, ch
values: { name: (errors && errors[0].name) || apiKeys[0].name },
}
);
- toastNotifications.addDanger(errorMessage);
+ notifications.toasts.addDanger(errorMessage);
}
};
diff --git a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/not_enabled/index.ts b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/not_enabled/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/not_enabled/index.ts
rename to x-pack/plugins/security/public/management/api_keys/api_keys_grid/not_enabled/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/not_enabled/not_enabled.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/not_enabled/not_enabled.tsx
similarity index 78%
rename from x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/not_enabled/not_enabled.tsx
rename to x-pack/plugins/security/public/management/api_keys/api_keys_grid/not_enabled/not_enabled.tsx
index c419e15450c1e..08fe542557757 100644
--- a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/not_enabled/not_enabled.tsx
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/not_enabled/not_enabled.tsx
@@ -7,9 +7,13 @@
import React from 'react';
import { EuiCallOut, EuiLink } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
-import { documentationLinks } from '../../services/documentation_links';
+import { DocumentationLinksService } from '../../documentation_links';
-export const NotEnabled: React.FunctionComponent = () => (
+interface Props {
+ docLinks: DocumentationLinksService;
+}
+
+export const NotEnabled: React.FunctionComponent = ({ docLinks }) => (
(
defaultMessage="Contact your system administrator and refer to the {link} to enable API keys."
values={{
link: (
-
+
({
+ APIKeysGridPage: (props: any) => `Page: ${JSON.stringify(props)}`,
+}));
+
+import { apiKeysManagementApp } from './api_keys_management_app';
+import { coreMock } from '../../../../../../src/core/public/mocks';
+
+describe('apiKeysManagementApp', () => {
+ it('create() returns proper management app descriptor', () => {
+ const { getStartServices } = coreMock.createSetup();
+
+ expect(apiKeysManagementApp.create({ getStartServices: getStartServices as any }))
+ .toMatchInlineSnapshot(`
+ Object {
+ "id": "api_keys",
+ "mount": [Function],
+ "order": 30,
+ "title": "API Keys",
+ }
+ `);
+ });
+
+ it('mount() works for the `grid` page', async () => {
+ const { getStartServices } = coreMock.createSetup();
+ const container = document.createElement('div');
+
+ const setBreadcrumbs = jest.fn();
+ const unmount = await apiKeysManagementApp
+ .create({ getStartServices: getStartServices as any })
+ .mount({
+ basePath: '/some-base-path',
+ element: container,
+ setBreadcrumbs,
+ });
+
+ expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
+ expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: '#/some-base-path', text: 'API Keys' }]);
+ expect(container).toMatchInlineSnapshot(`
+
+ Page: {"notifications":{"toasts":{}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"},"apiKeysAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}}}
+
+ `);
+
+ unmount();
+
+ expect(container).toMatchInlineSnapshot(``);
+ });
+});
diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx
new file mode 100644
index 0000000000000..35de732b84ce9
--- /dev/null
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx
@@ -0,0 +1,58 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { render, unmountComponentAtNode } from 'react-dom';
+import { i18n } from '@kbn/i18n';
+import { CoreSetup } from 'src/core/public';
+import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public';
+import { PluginStartDependencies } from '../../plugin';
+import { APIKeysGridPage } from './api_keys_grid';
+import { APIKeysAPIClient } from './api_keys_api_client';
+import { DocumentationLinksService } from './documentation_links';
+
+interface CreateParams {
+ getStartServices: CoreSetup['getStartServices'];
+}
+
+export const apiKeysManagementApp = Object.freeze({
+ id: 'api_keys',
+ create({ getStartServices }: CreateParams) {
+ return {
+ id: this.id,
+ order: 30,
+ title: i18n.translate('xpack.security.management.apiKeysTitle', {
+ defaultMessage: 'API Keys',
+ }),
+ async mount({ basePath, element, setBreadcrumbs }) {
+ const [{ docLinks, http, notifications, i18n: i18nStart }] = await getStartServices();
+ setBreadcrumbs([
+ {
+ text: i18n.translate('xpack.security.apiKeys.breadcrumb', {
+ defaultMessage: 'API Keys',
+ }),
+ href: `#${basePath}`,
+ },
+ ]);
+
+ render(
+
+
+ ,
+ element
+ );
+
+ return () => {
+ unmountComponentAtNode(element);
+ };
+ },
+ } as RegisterManagementAppArgs;
+ },
+});
diff --git a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/services/documentation_links.ts b/x-pack/plugins/security/public/management/api_keys/documentation_links.ts
similarity index 51%
rename from x-pack/legacy/plugins/security/public/views/management/api_keys_grid/services/documentation_links.ts
rename to x-pack/plugins/security/public/management/api_keys/documentation_links.ts
index 1f03763eb542a..4165c2a2372c9 100644
--- a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/services/documentation_links.ts
+++ b/x-pack/plugins/security/public/management/api_keys/documentation_links.ts
@@ -4,18 +4,20 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links';
+import { DocLinksStart } from 'src/core/public';
-class DocumentationLinksService {
- private esDocBasePath = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/`;
+export class DocumentationLinksService {
+ private readonly esDocBasePath: string;
- public getApiKeyServiceSettingsDocUrl(): string {
+ constructor(docLinks: DocLinksStart) {
+ this.esDocBasePath = `${docLinks.ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${docLinks.DOC_LINK_VERSION}/`;
+ }
+
+ public getApiKeyServiceSettingsDocUrl() {
return `${this.esDocBasePath}security-settings.html#api-key-service-settings`;
}
- public getCreateApiKeyDocUrl(): string {
+ public getCreateApiKeyDocUrl() {
return `${this.esDocBasePath}security-api-create-api-key.html`;
}
}
-
-export const documentationLinks = new DocumentationLinksService();
diff --git a/x-pack/plugins/security/public/management/api_keys/index.mock.ts b/x-pack/plugins/security/public/management/api_keys/index.mock.ts
new file mode 100644
index 0000000000000..3c11cd6bb9c65
--- /dev/null
+++ b/x-pack/plugins/security/public/management/api_keys/index.mock.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export { apiKeysAPIClientMock } from './api_keys_api_client.mock';
diff --git a/x-pack/plugins/security/public/management/api_keys/index.ts b/x-pack/plugins/security/public/management/api_keys/index.ts
new file mode 100644
index 0000000000000..e15da7d5eb409
--- /dev/null
+++ b/x-pack/plugins/security/public/management/api_keys/index.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export { apiKeysManagementApp } from './api_keys_management_app';
diff --git a/x-pack/plugins/security/public/management/index.ts b/x-pack/plugins/security/public/management/index.ts
new file mode 100644
index 0000000000000..e1a13d66e6883
--- /dev/null
+++ b/x-pack/plugins/security/public/management/index.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export { ManagementService } from './management_service';
+export { UserAPIClient } from './users/user_api_client';
diff --git a/x-pack/plugins/security/public/management/management_service.test.ts b/x-pack/plugins/security/public/management/management_service.test.ts
new file mode 100644
index 0000000000000..53c12ad7ab12c
--- /dev/null
+++ b/x-pack/plugins/security/public/management/management_service.test.ts
@@ -0,0 +1,226 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { BehaviorSubject } from 'rxjs';
+import { ManagementApp } from '../../../../../src/plugins/management/public';
+import { SecurityLicenseFeatures } from '../../common/licensing/license_features';
+import { ManagementService } from './management_service';
+import { usersManagementApp } from './users';
+
+import { coreMock } from '../../../../../src/core/public/mocks';
+import { licenseMock } from '../../common/licensing/index.mock';
+import { securityMock } from '../mocks';
+import { rolesManagementApp } from './roles';
+import { apiKeysManagementApp } from './api_keys';
+import { roleMappingsManagementApp } from './role_mappings';
+
+describe('ManagementService', () => {
+ describe('setup()', () => {
+ it('properly registers security section and its applications', () => {
+ const { fatalErrors, getStartServices } = coreMock.createSetup();
+ const { authc } = securityMock.createSetup();
+ const license = licenseMock.create();
+
+ const mockSection = { registerApp: jest.fn() };
+ const managementSetup = {
+ sections: {
+ getSection: jest.fn(),
+ getAllSections: jest.fn(),
+ register: jest.fn().mockReturnValue(mockSection),
+ },
+ };
+
+ const service = new ManagementService();
+ service.setup({
+ getStartServices: getStartServices as any,
+ license,
+ fatalErrors,
+ authc,
+ management: managementSetup,
+ });
+
+ expect(managementSetup.sections.register).toHaveBeenCalledTimes(1);
+ expect(managementSetup.sections.register).toHaveBeenCalledWith({
+ id: 'security',
+ title: 'Security',
+ order: 100,
+ euiIconType: 'securityApp',
+ });
+
+ expect(mockSection.registerApp).toHaveBeenCalledTimes(4);
+ expect(mockSection.registerApp).toHaveBeenCalledWith({
+ id: 'users',
+ mount: expect.any(Function),
+ order: 10,
+ title: 'Users',
+ });
+ expect(mockSection.registerApp).toHaveBeenCalledWith({
+ id: 'roles',
+ mount: expect.any(Function),
+ order: 20,
+ title: 'Roles',
+ });
+ expect(mockSection.registerApp).toHaveBeenCalledWith({
+ id: 'api_keys',
+ mount: expect.any(Function),
+ order: 30,
+ title: 'API Keys',
+ });
+ expect(mockSection.registerApp).toHaveBeenCalledWith({
+ id: 'role_mappings',
+ mount: expect.any(Function),
+ order: 40,
+ title: 'Role Mappings',
+ });
+ });
+ });
+
+ describe('start()', () => {
+ function startService(initialFeatures: Partial) {
+ const { fatalErrors, getStartServices } = coreMock.createSetup();
+
+ const licenseSubject = new BehaviorSubject(
+ (initialFeatures as unknown) as SecurityLicenseFeatures
+ );
+ const license = licenseMock.create();
+ license.features$ = licenseSubject;
+
+ const service = new ManagementService();
+ service.setup({
+ getStartServices: getStartServices as any,
+ license,
+ fatalErrors,
+ authc: securityMock.createSetup().authc,
+ management: {
+ sections: {
+ getSection: jest.fn(),
+ getAllSections: jest.fn(),
+ register: jest.fn().mockReturnValue({ registerApp: jest.fn() }),
+ },
+ },
+ });
+
+ const getMockedApp = () => {
+ // All apps are enabled by default.
+ let enabled = true;
+ return ({
+ get enabled() {
+ return enabled;
+ },
+ enable: jest.fn().mockImplementation(() => {
+ enabled = true;
+ }),
+ disable: jest.fn().mockImplementation(() => {
+ enabled = false;
+ }),
+ } as unknown) as jest.Mocked;
+ };
+ const mockApps = new Map>([
+ [usersManagementApp.id, getMockedApp()],
+ [rolesManagementApp.id, getMockedApp()],
+ [apiKeysManagementApp.id, getMockedApp()],
+ [roleMappingsManagementApp.id, getMockedApp()],
+ ] as Array<[string, jest.Mocked]>);
+
+ service.start({
+ management: {
+ sections: {
+ getSection: jest
+ .fn()
+ .mockReturnValue({ getApp: jest.fn().mockImplementation(id => mockApps.get(id)) }),
+ getAllSections: jest.fn(),
+ navigateToApp: jest.fn(),
+ },
+ legacy: undefined,
+ },
+ });
+
+ return {
+ mockApps,
+ updateFeatures(features: Partial) {
+ licenseSubject.next((features as unknown) as SecurityLicenseFeatures);
+ },
+ };
+ }
+
+ it('does not do anything if `showLinks` is `true` at `start`', () => {
+ const { mockApps } = startService({ showLinks: true, showRoleMappingsManagement: true });
+ for (const [, mockApp] of mockApps) {
+ expect(mockApp.enable).not.toHaveBeenCalled();
+ expect(mockApp.disable).not.toHaveBeenCalled();
+ expect(mockApp.enabled).toBe(true);
+ }
+ });
+
+ it('disables all apps if `showLinks` is `false` at `start`', () => {
+ const { mockApps } = startService({ showLinks: false, showRoleMappingsManagement: true });
+ for (const [, mockApp] of mockApps) {
+ expect(mockApp.enabled).toBe(false);
+ }
+ });
+
+ it('disables only Role Mappings app if `showLinks` is `true`, but `showRoleMappingsManagement` is `false` at `start`', () => {
+ const { mockApps } = startService({ showLinks: true, showRoleMappingsManagement: false });
+ for (const [appId, mockApp] of mockApps) {
+ expect(mockApp.enabled).toBe(appId !== roleMappingsManagementApp.id);
+ }
+ });
+
+ it('apps are disabled if `showLinks` changes after `start`', () => {
+ const { mockApps, updateFeatures } = startService({
+ showLinks: true,
+ showRoleMappingsManagement: true,
+ });
+ for (const [, mockApp] of mockApps) {
+ expect(mockApp.enabled).toBe(true);
+ }
+
+ updateFeatures({ showLinks: false, showRoleMappingsManagement: false });
+
+ for (const [, mockApp] of mockApps) {
+ expect(mockApp.enabled).toBe(false);
+ }
+ });
+
+ it('role mappings app is disabled if `showRoleMappingsManagement` changes after `start`', () => {
+ const { mockApps, updateFeatures } = startService({
+ showLinks: true,
+ showRoleMappingsManagement: true,
+ });
+ for (const [, mockApp] of mockApps) {
+ expect(mockApp.enabled).toBe(true);
+ }
+
+ updateFeatures({ showLinks: true, showRoleMappingsManagement: false });
+
+ for (const [appId, mockApp] of mockApps) {
+ expect(mockApp.enabled).toBe(appId !== roleMappingsManagementApp.id);
+ }
+ });
+
+ it('apps are re-enabled if `showLinks` eventually transitions to `true` after `start`', () => {
+ const { mockApps, updateFeatures } = startService({
+ showLinks: true,
+ showRoleMappingsManagement: true,
+ });
+ for (const [, mockApp] of mockApps) {
+ expect(mockApp.enabled).toBe(true);
+ }
+
+ updateFeatures({ showLinks: false, showRoleMappingsManagement: false });
+
+ for (const [, mockApp] of mockApps) {
+ expect(mockApp.enabled).toBe(false);
+ }
+
+ updateFeatures({ showLinks: true, showRoleMappingsManagement: true });
+
+ for (const [, mockApp] of mockApps) {
+ expect(mockApp.enabled).toBe(true);
+ }
+ });
+ });
+});
diff --git a/x-pack/plugins/security/public/management/management_service.ts b/x-pack/plugins/security/public/management/management_service.ts
new file mode 100644
index 0000000000000..5ad3681590fbf
--- /dev/null
+++ b/x-pack/plugins/security/public/management/management_service.ts
@@ -0,0 +1,95 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { Subscription } from 'rxjs';
+import { i18n } from '@kbn/i18n';
+import { CoreSetup, FatalErrorsSetup } from 'src/core/public';
+import {
+ ManagementApp,
+ ManagementSetup,
+ ManagementStart,
+} from '../../../../../src/plugins/management/public';
+import { SecurityLicense } from '../../common/licensing';
+import { AuthenticationServiceSetup } from '../authentication';
+import { PluginStartDependencies } from '../plugin';
+import { apiKeysManagementApp } from './api_keys';
+import { roleMappingsManagementApp } from './role_mappings';
+import { rolesManagementApp } from './roles';
+import { usersManagementApp } from './users';
+
+interface SetupParams {
+ management: ManagementSetup;
+ license: SecurityLicense;
+ authc: AuthenticationServiceSetup;
+ fatalErrors: FatalErrorsSetup;
+ getStartServices: CoreSetup['getStartServices'];
+}
+
+interface StartParams {
+ management: ManagementStart;
+}
+
+export class ManagementService {
+ private license!: SecurityLicense;
+ private licenseFeaturesSubscription?: Subscription;
+
+ setup({ getStartServices, management, authc, license, fatalErrors }: SetupParams) {
+ this.license = license;
+
+ const securitySection = management.sections.register({
+ id: 'security',
+ title: i18n.translate('xpack.security.management.securityTitle', {
+ defaultMessage: 'Security',
+ }),
+ order: 100,
+ euiIconType: 'securityApp',
+ });
+
+ securitySection.registerApp(usersManagementApp.create({ authc, getStartServices }));
+ securitySection.registerApp(
+ rolesManagementApp.create({ fatalErrors, license, getStartServices })
+ );
+ securitySection.registerApp(apiKeysManagementApp.create({ getStartServices }));
+ securitySection.registerApp(roleMappingsManagementApp.create({ getStartServices }));
+ }
+
+ start({ management }: StartParams) {
+ this.licenseFeaturesSubscription = this.license.features$.subscribe(async features => {
+ const securitySection = management.sections.getSection('security')!;
+
+ const securityManagementAppsStatuses: Array<[ManagementApp, boolean]> = [
+ [securitySection.getApp(usersManagementApp.id)!, features.showLinks],
+ [securitySection.getApp(rolesManagementApp.id)!, features.showLinks],
+ [securitySection.getApp(apiKeysManagementApp.id)!, features.showLinks],
+ [
+ securitySection.getApp(roleMappingsManagementApp.id)!,
+ features.showLinks && features.showRoleMappingsManagement,
+ ],
+ ];
+
+ // Iterate over all registered apps and update their enable status depending on the available
+ // license features.
+ for (const [app, enableStatus] of securityManagementAppsStatuses) {
+ if (app.enabled === enableStatus) {
+ continue;
+ }
+
+ if (enableStatus) {
+ app.enable();
+ } else {
+ app.disable();
+ }
+ }
+ });
+ }
+
+ stop() {
+ if (this.licenseFeaturesSubscription) {
+ this.licenseFeaturesSubscription.unsubscribe();
+ this.licenseFeaturesSubscription = undefined;
+ }
+ }
+}
diff --git a/x-pack/legacy/plugins/security/public/views/management/management_urls.ts b/x-pack/plugins/security/public/management/management_urls.ts
similarity index 74%
rename from x-pack/legacy/plugins/security/public/views/management/management_urls.ts
rename to x-pack/plugins/security/public/management/management_urls.ts
index 881740c0b2895..0d4e3fc920bdb 100644
--- a/x-pack/legacy/plugins/security/public/views/management/management_urls.ts
+++ b/x-pack/plugins/security/public/management/management_urls.ts
@@ -4,19 +4,18 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export const MANAGEMENT_PATH = '/management';
-export const SECURITY_PATH = `${MANAGEMENT_PATH}/security`;
+const MANAGEMENT_PATH = '/management';
+const SECURITY_PATH = `${MANAGEMENT_PATH}/security`;
export const ROLES_PATH = `${SECURITY_PATH}/roles`;
export const EDIT_ROLES_PATH = `${ROLES_PATH}/edit`;
export const CLONE_ROLES_PATH = `${ROLES_PATH}/clone`;
export const USERS_PATH = `${SECURITY_PATH}/users`;
export const EDIT_USERS_PATH = `${USERS_PATH}/edit`;
-export const API_KEYS_PATH = `${SECURITY_PATH}/api_keys`;
export const ROLE_MAPPINGS_PATH = `${SECURITY_PATH}/role_mappings`;
-export const CREATE_ROLE_MAPPING_PATH = `${ROLE_MAPPINGS_PATH}/edit`;
+const CREATE_ROLE_MAPPING_PATH = `${ROLE_MAPPINGS_PATH}/edit`;
export const getEditRoleHref = (roleName: string) =>
- `#${EDIT_ROLES_PATH}/${encodeURIComponent(roleName)}`;
+ `#${ROLES_PATH}/edit/${encodeURIComponent(roleName)}`;
export const getCreateRoleMappingHref = () => `#${CREATE_ROLE_MAPPING_PATH}`;
diff --git a/x-pack/plugins/security/public/management/role_mappings/_index.scss b/x-pack/plugins/security/public/management/role_mappings/_index.scss
new file mode 100644
index 0000000000000..bae6effcd2ec5
--- /dev/null
+++ b/x-pack/plugins/security/public/management/role_mappings/_index.scss
@@ -0,0 +1 @@
+@import './edit_role_mapping/index';
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/components/delete_provider/delete_provider.test.tsx b/x-pack/plugins/security/public/management/role_mappings/components/delete_provider/delete_provider.test.tsx
similarity index 64%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/components/delete_provider/delete_provider.test.tsx
rename to x-pack/plugins/security/public/management/role_mappings/components/delete_provider/delete_provider.test.tsx
index b826d68053e27..69142b1ad610e 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/components/delete_provider/delete_provider.test.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/components/delete_provider/delete_provider.test.tsx
@@ -5,24 +5,15 @@
*/
import React from 'react';
-import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers';
-import { DeleteProvider } from '.';
-import { RoleMappingsAPI } from '../../../../../lib/role_mappings_api';
-import { RoleMapping } from '../../../../../../common/model';
import { EuiConfirmModal } from '@elastic/eui';
-import { findTestSubject } from 'test_utils/find_test_subject';
import { act } from '@testing-library/react';
-import { toastNotifications } from 'ui/notify';
-
-jest.mock('ui/notify', () => {
- return {
- toastNotifications: {
- addError: jest.fn(),
- addSuccess: jest.fn(),
- addDanger: jest.fn(),
- },
- };
-});
+import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers';
+import { findTestSubject } from 'test_utils/find_test_subject';
+import { RoleMapping } from '../../../../../common/model';
+import { DeleteProvider } from '.';
+
+import { roleMappingsAPIClientMock } from '../../index.mock';
+import { coreMock } from '../../../../../../../../src/core/public/mocks';
describe('DeleteProvider', () => {
beforeEach(() => {
@@ -30,17 +21,14 @@ describe('DeleteProvider', () => {
});
it('allows a single role mapping to be deleted', async () => {
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.deleteRoleMappings.mockResolvedValue([{ name: 'delete-me', success: true }]);
+
+ const notifications = coreMock.createStart().notifications;
+
const props = {
- roleMappingsAPI: ({
- deleteRoleMappings: jest.fn().mockReturnValue(
- Promise.resolve([
- {
- name: 'delete-me',
- success: true,
- },
- ])
- ),
- } as unknown) as RoleMappingsAPI,
+ roleMappingsAPI,
+ notifications,
};
const roleMappingsToDelete = [
@@ -79,11 +67,10 @@ describe('DeleteProvider', () => {
expect(props.roleMappingsAPI.deleteRoleMappings).toHaveBeenCalledWith(['delete-me']);
- const notifications = toastNotifications as jest.Mocked;
- expect(notifications.addError).toHaveBeenCalledTimes(0);
- expect(notifications.addDanger).toHaveBeenCalledTimes(0);
- expect(notifications.addSuccess).toHaveBeenCalledTimes(1);
- expect(notifications.addSuccess.mock.calls[0]).toMatchInlineSnapshot(`
+ expect(notifications.toasts.addError).toHaveBeenCalledTimes(0);
+ expect(notifications.toasts.addDanger).toHaveBeenCalledTimes(0);
+ expect(notifications.toasts.addSuccess).toHaveBeenCalledTimes(1);
+ expect(notifications.toasts.addSuccess.mock.calls[0]).toMatchInlineSnapshot(`
Array [
Object {
"data-test-subj": "deletedRoleMappingSuccessToast",
@@ -94,21 +81,23 @@ describe('DeleteProvider', () => {
});
it('allows multiple role mappings to be deleted', async () => {
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.deleteRoleMappings.mockResolvedValue([
+ {
+ name: 'delete-me',
+ success: true,
+ },
+ {
+ name: 'delete-me-too',
+ success: true,
+ },
+ ]);
+
+ const notifications = coreMock.createStart().notifications;
+
const props = {
- roleMappingsAPI: ({
- deleteRoleMappings: jest.fn().mockReturnValue(
- Promise.resolve([
- {
- name: 'delete-me',
- success: true,
- },
- {
- name: 'delete-me-too',
- success: true,
- },
- ])
- ),
- } as unknown) as RoleMappingsAPI,
+ roleMappingsAPI,
+ notifications,
};
const roleMappingsToDelete = [
@@ -152,11 +141,11 @@ describe('DeleteProvider', () => {
'delete-me',
'delete-me-too',
]);
- const notifications = toastNotifications as jest.Mocked;
- expect(notifications.addError).toHaveBeenCalledTimes(0);
- expect(notifications.addDanger).toHaveBeenCalledTimes(0);
- expect(notifications.addSuccess).toHaveBeenCalledTimes(1);
- expect(notifications.addSuccess.mock.calls[0]).toMatchInlineSnapshot(`
+
+ expect(notifications.toasts.addError).toHaveBeenCalledTimes(0);
+ expect(notifications.toasts.addDanger).toHaveBeenCalledTimes(0);
+ expect(notifications.toasts.addSuccess).toHaveBeenCalledTimes(1);
+ expect(notifications.toasts.addSuccess.mock.calls[0]).toMatchInlineSnapshot(`
Array [
Object {
"data-test-subj": "deletedRoleMappingSuccessToast",
@@ -167,22 +156,24 @@ describe('DeleteProvider', () => {
});
it('handles mixed success/failure conditions', async () => {
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.deleteRoleMappings.mockResolvedValue([
+ {
+ name: 'delete-me',
+ success: true,
+ },
+ {
+ name: 'i-wont-work',
+ success: false,
+ error: new Error('something went wrong. sad.'),
+ },
+ ]);
+
+ const notifications = coreMock.createStart().notifications;
+
const props = {
- roleMappingsAPI: ({
- deleteRoleMappings: jest.fn().mockReturnValue(
- Promise.resolve([
- {
- name: 'delete-me',
- success: true,
- },
- {
- name: 'i-wont-work',
- success: false,
- error: new Error('something went wrong. sad.'),
- },
- ])
- ),
- } as unknown) as RoleMappingsAPI,
+ roleMappingsAPI,
+ notifications,
};
const roleMappingsToDelete = [
@@ -223,10 +214,9 @@ describe('DeleteProvider', () => {
'i-wont-work',
]);
- const notifications = toastNotifications as jest.Mocked;
- expect(notifications.addError).toHaveBeenCalledTimes(0);
- expect(notifications.addSuccess).toHaveBeenCalledTimes(1);
- expect(notifications.addSuccess.mock.calls[0]).toMatchInlineSnapshot(`
+ expect(notifications.toasts.addError).toHaveBeenCalledTimes(0);
+ expect(notifications.toasts.addSuccess).toHaveBeenCalledTimes(1);
+ expect(notifications.toasts.addSuccess.mock.calls[0]).toMatchInlineSnapshot(`
Array [
Object {
"data-test-subj": "deletedRoleMappingSuccessToast",
@@ -235,8 +225,8 @@ describe('DeleteProvider', () => {
]
`);
- expect(notifications.addDanger).toHaveBeenCalledTimes(1);
- expect(notifications.addDanger.mock.calls[0]).toMatchInlineSnapshot(`
+ expect(notifications.toasts.addDanger).toHaveBeenCalledTimes(1);
+ expect(notifications.toasts.addDanger.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"Error deleting role mapping 'i-wont-work'",
]
@@ -244,12 +234,13 @@ describe('DeleteProvider', () => {
});
it('handles errors calling the API', async () => {
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.deleteRoleMappings.mockRejectedValue(new Error('AHHHHH'));
+
+ const notifications = coreMock.createStart().notifications;
const props = {
- roleMappingsAPI: ({
- deleteRoleMappings: jest.fn().mockImplementation(() => {
- throw new Error('AHHHHH');
- }),
- } as unknown) as RoleMappingsAPI,
+ roleMappingsAPI,
+ notifications,
};
const roleMappingsToDelete = [
@@ -284,12 +275,11 @@ describe('DeleteProvider', () => {
expect(props.roleMappingsAPI.deleteRoleMappings).toHaveBeenCalledWith(['delete-me']);
- const notifications = toastNotifications as jest.Mocked;
- expect(notifications.addDanger).toHaveBeenCalledTimes(0);
- expect(notifications.addSuccess).toHaveBeenCalledTimes(0);
+ expect(notifications.toasts.addDanger).toHaveBeenCalledTimes(0);
+ expect(notifications.toasts.addSuccess).toHaveBeenCalledTimes(0);
- expect(notifications.addError).toHaveBeenCalledTimes(1);
- expect(notifications.addError.mock.calls[0]).toMatchInlineSnapshot(`
+ expect(notifications.toasts.addError).toHaveBeenCalledTimes(1);
+ expect(notifications.toasts.addError.mock.calls[0]).toMatchInlineSnapshot(`
Array [
[Error: AHHHHH],
Object {
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/components/delete_provider/delete_provider.tsx b/x-pack/plugins/security/public/management/role_mappings/components/delete_provider/delete_provider.tsx
similarity index 93%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/components/delete_provider/delete_provider.tsx
rename to x-pack/plugins/security/public/management/role_mappings/components/delete_provider/delete_provider.tsx
index 2072cedeab462..860fe22cb8032 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/components/delete_provider/delete_provider.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/components/delete_provider/delete_provider.tsx
@@ -6,13 +6,14 @@
import React, { Fragment, useRef, useState, ReactElement } from 'react';
import { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui';
-import { toastNotifications } from 'ui/notify';
import { i18n } from '@kbn/i18n';
-import { RoleMapping } from '../../../../../../common/model';
-import { RoleMappingsAPI } from '../../../../../lib/role_mappings_api';
+import { NotificationsStart } from 'src/core/public';
+import { RoleMapping } from '../../../../../common/model';
+import { RoleMappingsAPIClient } from '../../role_mappings_api_client';
interface Props {
- roleMappingsAPI: RoleMappingsAPI;
+ roleMappingsAPI: PublicMethodsOf;
+ notifications: NotificationsStart;
children: (deleteMappings: DeleteRoleMappings) => ReactElement;
}
@@ -23,7 +24,11 @@ export type DeleteRoleMappings = (
type OnSuccessCallback = (deletedRoleMappings: string[]) => void;
-export const DeleteProvider: React.FunctionComponent = ({ roleMappingsAPI, children }) => {
+export const DeleteProvider: React.FunctionComponent = ({
+ roleMappingsAPI,
+ children,
+ notifications,
+}) => {
const [roleMappings, setRoleMappings] = useState([]);
const [isModalOpen, setIsModalOpen] = useState(false);
const [isDeleteInProgress, setIsDeleteInProgress] = useState(false);
@@ -55,7 +60,7 @@ export const DeleteProvider: React.FunctionComponent = ({ roleMappingsAPI
try {
result = await roleMappingsAPI.deleteRoleMappings(roleMappings.map(rm => rm.name));
} catch (e) {
- toastNotifications.addError(e, {
+ notifications.toasts.addError(e, {
title: i18n.translate(
'xpack.security.management.roleMappings.deleteRoleMapping.unknownError',
{
@@ -92,7 +97,7 @@ export const DeleteProvider: React.FunctionComponent = ({ roleMappingsAPI
values: { name: successfulDeletes[0].name },
}
);
- toastNotifications.addSuccess({
+ notifications.toasts.addSuccess({
title: successMessage,
'data-test-subj': 'deletedRoleMappingSuccessToast',
});
@@ -121,7 +126,7 @@ export const DeleteProvider: React.FunctionComponent = ({ roleMappingsAPI
values: { name: erroredDeletes[0].name },
}
);
- toastNotifications.addDanger(errorMessage);
+ notifications.toasts.addDanger(errorMessage);
}
};
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/components/delete_provider/index.ts b/x-pack/plugins/security/public/management/role_mappings/components/delete_provider/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/components/delete_provider/index.ts
rename to x-pack/plugins/security/public/management/role_mappings/components/delete_provider/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/components/index.ts b/x-pack/plugins/security/public/management/role_mappings/components/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/components/index.ts
rename to x-pack/plugins/security/public/management/role_mappings/components/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/components/no_compatible_realms/index.ts b/x-pack/plugins/security/public/management/role_mappings/components/no_compatible_realms/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/components/no_compatible_realms/index.ts
rename to x-pack/plugins/security/public/management/role_mappings/components/no_compatible_realms/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/components/no_compatible_realms/no_compatible_realms.tsx b/x-pack/plugins/security/public/management/role_mappings/components/no_compatible_realms/no_compatible_realms.tsx
similarity index 78%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/components/no_compatible_realms/no_compatible_realms.tsx
rename to x-pack/plugins/security/public/management/role_mappings/components/no_compatible_realms/no_compatible_realms.tsx
index 969832b3ecbae..5e14b0c179bfd 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/components/no_compatible_realms/no_compatible_realms.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/components/no_compatible_realms/no_compatible_realms.tsx
@@ -7,9 +7,13 @@
import React from 'react';
import { EuiCallOut, EuiLink } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
-import { documentationLinks } from '../../services/documentation_links';
+import { DocumentationLinksService } from '../../documentation_links';
-export const NoCompatibleRealms: React.FunctionComponent = () => (
+interface Props {
+ docLinks: DocumentationLinksService;
+}
+
+export const NoCompatibleRealms: React.FunctionComponent = ({ docLinks }: Props) => (
(
defaultMessage="Role mappings will not be applied to any users. Contact your system administrator and refer to the {link} for more information."
values={{
link: (
-
+
{
+ let rolesAPI: PublicMethodsOf;
+ beforeEach(() => {
+ rolesAPI = rolesAPIClientMock.create();
+ (rolesAPI as jest.Mocked).getRoles.mockResolvedValue([
+ { name: 'foo_role' },
+ { name: 'bar role' },
+ ] as Role[]);
+ });
+
+ it('allows a role mapping to be created', async () => {
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.saveRoleMapping.mockResolvedValue(null);
+ roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({
+ canManageRoleMappings: true,
+ hasCompatibleRealms: true,
+ canUseInlineScripts: true,
+ canUseStoredScripts: true,
+ });
+
+ const { docLinks, notifications } = coreMock.createStart();
+ const wrapper = mountWithIntl(
+
+ );
+
+ await nextTick();
+ wrapper.update();
+
+ findTestSubject(wrapper, 'roleMappingFormNameInput').simulate('change', {
+ target: { value: 'my-role-mapping' },
+ });
+
+ (wrapper
+ .find(EuiComboBox)
+ .filter('[data-test-subj="roleMappingFormRoleComboBox"]')
+ .props() as any).onChange([{ label: 'foo_role' }]);
+
+ findTestSubject(wrapper, 'roleMappingsAddRuleButton').simulate('click');
+
+ findTestSubject(wrapper, 'saveRoleMappingButton').simulate('click');
+
+ expect(roleMappingsAPI.saveRoleMapping).toHaveBeenCalledWith({
+ name: 'my-role-mapping',
+ enabled: true,
+ roles: ['foo_role'],
+ role_templates: [],
+ rules: {
+ all: [{ field: { username: '*' } }],
+ },
+ metadata: {},
+ });
+ });
+
+ it('allows a role mapping to be updated', async () => {
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.saveRoleMapping.mockResolvedValue(null);
+ roleMappingsAPI.getRoleMapping.mockResolvedValue({
+ name: 'foo',
+ role_templates: [
+ {
+ template: { id: 'foo' },
+ },
+ ],
+ enabled: true,
+ rules: {
+ any: [{ field: { 'metadata.someCustomOption': [false, true, 'asdf'] } }],
+ },
+ metadata: {
+ foo: 'bar',
+ bar: 'baz',
+ },
+ });
+ roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({
+ canManageRoleMappings: true,
+ hasCompatibleRealms: true,
+ canUseInlineScripts: true,
+ canUseStoredScripts: true,
+ });
+
+ const { docLinks, notifications } = coreMock.createStart();
+ const wrapper = mountWithIntl(
+
+ );
+
+ await nextTick();
+ wrapper.update();
+
+ findTestSubject(wrapper, 'switchToRolesButton').simulate('click');
+
+ (wrapper
+ .find(EuiComboBox)
+ .filter('[data-test-subj="roleMappingFormRoleComboBox"]')
+ .props() as any).onChange([{ label: 'foo_role' }]);
+
+ findTestSubject(wrapper, 'roleMappingsAddRuleButton').simulate('click');
+ wrapper.find('button[id="addRuleOption"]').simulate('click');
+
+ findTestSubject(wrapper, 'saveRoleMappingButton').simulate('click');
+
+ expect(roleMappingsAPI.saveRoleMapping).toHaveBeenCalledWith({
+ name: 'foo',
+ enabled: true,
+ roles: ['foo_role'],
+ role_templates: [],
+ rules: {
+ any: [
+ { field: { 'metadata.someCustomOption': [false, true, 'asdf'] } },
+ { field: { username: '*' } },
+ ],
+ },
+ metadata: {
+ foo: 'bar',
+ bar: 'baz',
+ },
+ });
+ });
+
+ it('renders a permission denied message when unauthorized to manage role mappings', async () => {
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({
+ canManageRoleMappings: false,
+ hasCompatibleRealms: true,
+ });
+
+ const { docLinks, notifications } = coreMock.createStart();
+ const wrapper = mountWithIntl(
+
+ );
+ expect(wrapper.find(SectionLoading)).toHaveLength(1);
+ expect(wrapper.find(PermissionDenied)).toHaveLength(0);
+
+ await nextTick();
+ wrapper.update();
+
+ expect(wrapper.find(SectionLoading)).toHaveLength(0);
+ expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0);
+ expect(wrapper.find(PermissionDenied)).toHaveLength(1);
+ });
+
+ it('renders a warning when there are no compatible realms enabled', async () => {
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({
+ canManageRoleMappings: true,
+ hasCompatibleRealms: false,
+ });
+
+ const { docLinks, notifications } = coreMock.createStart();
+ const wrapper = mountWithIntl(
+
+ );
+ expect(wrapper.find(SectionLoading)).toHaveLength(1);
+ expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0);
+
+ await nextTick();
+ wrapper.update();
+
+ expect(wrapper.find(SectionLoading)).toHaveLength(0);
+ expect(wrapper.find(NoCompatibleRealms)).toHaveLength(1);
+ });
+
+ it('renders a warning when editing a mapping with a stored role template, when stored scripts are disabled', async () => {
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.getRoleMapping.mockResolvedValue({
+ name: 'foo',
+ role_templates: [
+ {
+ template: { id: 'foo' },
+ },
+ ],
+ enabled: true,
+ rules: {
+ field: { username: '*' },
+ },
+ });
+ roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({
+ canManageRoleMappings: true,
+ hasCompatibleRealms: true,
+ canUseInlineScripts: true,
+ canUseStoredScripts: false,
+ });
+
+ const { docLinks, notifications } = coreMock.createStart();
+ const wrapper = mountWithIntl(
+
+ );
+
+ expect(findTestSubject(wrapper, 'roleMappingInlineScriptsDisabled')).toHaveLength(0);
+ expect(findTestSubject(wrapper, 'roleMappingStoredScriptsDisabled')).toHaveLength(0);
+
+ await nextTick();
+ wrapper.update();
+
+ expect(findTestSubject(wrapper, 'roleMappingInlineScriptsDisabled')).toHaveLength(0);
+ expect(findTestSubject(wrapper, 'roleMappingStoredScriptsDisabled')).toHaveLength(1);
+ });
+
+ it('renders a warning when editing a mapping with an inline role template, when inline scripts are disabled', async () => {
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.getRoleMapping.mockResolvedValue({
+ name: 'foo',
+ role_templates: [
+ {
+ template: { source: 'foo' },
+ },
+ ],
+ enabled: true,
+ rules: {
+ field: { username: '*' },
+ },
+ });
+ roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({
+ canManageRoleMappings: true,
+ hasCompatibleRealms: true,
+ canUseInlineScripts: false,
+ canUseStoredScripts: true,
+ });
+
+ const { docLinks, notifications } = coreMock.createStart();
+ const wrapper = mountWithIntl(
+
+ );
+
+ expect(findTestSubject(wrapper, 'roleMappingInlineScriptsDisabled')).toHaveLength(0);
+ expect(findTestSubject(wrapper, 'roleMappingStoredScriptsDisabled')).toHaveLength(0);
+
+ await nextTick();
+ wrapper.update();
+
+ expect(findTestSubject(wrapper, 'roleMappingInlineScriptsDisabled')).toHaveLength(1);
+ expect(findTestSubject(wrapper, 'roleMappingStoredScriptsDisabled')).toHaveLength(0);
+ });
+
+ it('renders the visual editor by default for simple rule sets', async () => {
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.getRoleMapping.mockResolvedValue({
+ name: 'foo',
+ roles: ['superuser'],
+ enabled: true,
+ rules: {
+ all: [
+ {
+ field: {
+ username: '*',
+ },
+ },
+ {
+ field: {
+ dn: null,
+ },
+ },
+ {
+ field: {
+ realm: ['ldap', 'pki', null, 12],
+ },
+ },
+ ],
+ },
+ });
+ roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({
+ canManageRoleMappings: true,
+ hasCompatibleRealms: true,
+ canUseInlineScripts: true,
+ canUseStoredScripts: true,
+ });
+
+ const { docLinks, notifications } = coreMock.createStart();
+ const wrapper = mountWithIntl(
+
+ );
+
+ await nextTick();
+ wrapper.update();
+
+ expect(wrapper.find(VisualRuleEditor)).toHaveLength(1);
+ expect(wrapper.find(JSONRuleEditor)).toHaveLength(0);
+ });
+
+ it('renders the JSON editor by default for complex rule sets', async () => {
+ const createRule = (depth: number): Record => {
+ if (depth > 0) {
+ const rule = {
+ all: [
+ {
+ field: {
+ username: '*',
+ },
+ },
+ ],
+ } as Record;
+
+ const subRule = createRule(depth - 1);
+ if (subRule) {
+ rule.all.push(subRule);
+ }
+
+ return rule;
+ }
+ return null as any;
+ };
+
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.getRoleMapping.mockResolvedValue({
+ name: 'foo',
+ roles: ['superuser'],
+ enabled: true,
+ rules: createRule(10),
+ });
+ roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({
+ canManageRoleMappings: true,
+ hasCompatibleRealms: true,
+ canUseInlineScripts: true,
+ canUseStoredScripts: true,
+ });
+
+ const { docLinks, notifications } = coreMock.createStart();
+ const wrapper = mountWithIntl(
+
+ );
+
+ await nextTick();
+ wrapper.update();
+
+ expect(wrapper.find(VisualRuleEditor)).toHaveLength(0);
+ expect(wrapper.find(JSONRuleEditor)).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/edit_role_mapping_page.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx
similarity index 87%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/edit_role_mapping_page.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx
index b8a75a4ad9fdf..142b53cbb50f2 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/edit_role_mapping_page.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx
@@ -19,20 +19,21 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
-import { toastNotifications } from 'ui/notify';
-import { RoleMapping } from '../../../../../../common/model';
-import { RoleMappingsAPI } from '../../../../../lib/role_mappings_api';
+import { NotificationsStart } from 'src/core/public';
+import { RoleMapping } from '../../../../common/model';
import { RuleEditorPanel } from './rule_editor_panel';
import {
NoCompatibleRealms,
PermissionDenied,
DeleteProvider,
SectionLoading,
-} from '../../components';
-import { ROLE_MAPPINGS_PATH } from '../../../management_urls';
-import { validateRoleMappingForSave } from '../services/role_mapping_validation';
+} from '../components';
+import { RolesAPIClient } from '../../roles';
+import { ROLE_MAPPINGS_PATH } from '../../management_urls';
+import { validateRoleMappingForSave } from './services/role_mapping_validation';
import { MappingInfoPanel } from './mapping_info_panel';
-import { documentationLinks } from '../../services/documentation_links';
+import { DocumentationLinksService } from '../documentation_links';
+import { RoleMappingsAPIClient } from '../role_mappings_api_client';
interface State {
loadState: 'loading' | 'permissionDenied' | 'ready' | 'saveInProgress';
@@ -50,7 +51,10 @@ interface State {
interface Props {
name?: string;
- roleMappingsAPI: RoleMappingsAPI;
+ roleMappingsAPI: PublicMethodsOf;
+ rolesAPIClient: PublicMethodsOf;
+ notifications: NotificationsStart;
+ docLinks: DocumentationLinksService;
}
export class EditRoleMappingPage extends Component {
@@ -74,6 +78,12 @@ export class EditRoleMappingPage extends Component {
this.loadAppData();
}
+ public async componentDidUpdate(prevProps: Props) {
+ if (prevProps.name !== this.props.name) {
+ await this.loadAppData();
+ }
+ }
+
public render() {
const { loadState } = this.state;
@@ -101,6 +111,8 @@ export class EditRoleMappingPage extends Component {
validateForm={this.state.validateForm}
canUseInlineScripts={this.state.canUseInlineScripts}
canUseStoredScripts={this.state.canUseStoredScripts}
+ rolesAPIClient={this.props.rolesAPIClient}
+ docLinks={this.props.docLinks}
/>
{
},
})
}
+ docLinks={this.props.docLinks}
/>
{this.getFormButtons()}
@@ -149,7 +162,7 @@ export class EditRoleMappingPage extends Component {
values={{
learnMoreLink: (
@@ -166,7 +179,7 @@ export class EditRoleMappingPage extends Component {
{!this.state.hasCompatibleRealms && (
<>
-
+
>
)}
@@ -201,7 +214,10 @@ export class EditRoleMappingPage extends Component {
{this.editingExistingRoleMapping() && (
-
+
{deleteRoleMappingsPrompt => {
return (
{
this.props.roleMappingsAPI
.saveRoleMapping(this.state.roleMapping)
.then(() => {
- toastNotifications.addSuccess({
+ this.props.notifications.toasts.addSuccess({
title: i18n.translate('xpack.security.management.editRoleMapping.saveSuccess', {
defaultMessage: `Saved role mapping '{roleMappingName}'`,
values: {
@@ -264,7 +280,7 @@ export class EditRoleMappingPage extends Component {
this.backToRoleMappingsList();
})
.catch(e => {
- toastNotifications.addError(e, {
+ this.props.notifications.toasts.addError(e, {
title: i18n.translate('xpack.security.management.editRoleMapping.saveError', {
defaultMessage: `Error saving role mapping`,
}),
@@ -312,7 +328,7 @@ export class EditRoleMappingPage extends Component {
roleMapping,
});
} catch (e) {
- toastNotifications.addDanger({
+ this.props.notifications.toasts.addDanger({
title: i18n.translate(
'xpack.security.management.editRoleMapping.table.fetchingRoleMappingsErrorMessage',
{
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/index.ts b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/index.ts
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/mapping_info_panel/index.ts b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/mapping_info_panel/index.ts
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/mapping_info_panel/mapping_info_panel.test.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.test.tsx
similarity index 82%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/mapping_info_panel/mapping_info_panel.test.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.test.tsx
index d821b33ace6a7..9b62ca27ca569 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/mapping_info_panel/mapping_info_panel.test.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.test.tsx
@@ -6,21 +6,27 @@
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
-import { MappingInfoPanel } from '.';
-import { RoleMapping } from '../../../../../../../common/model';
import { findTestSubject } from 'test_utils/find_test_subject';
+import { Role, RoleMapping } from '../../../../../common/model';
+import { RolesAPIClient } from '../../../roles';
+import { DocumentationLinksService } from '../../documentation_links';
import { RoleSelector } from '../role_selector';
import { RoleTemplateEditor } from '../role_selector/role_template_editor';
+import { MappingInfoPanel } from '.';
-jest.mock('../../../../../../lib/roles_api', () => {
- return {
- RolesApi: {
- getRoles: () => Promise.resolve([{ name: 'foo_role' }, { name: 'bar role' }]),
- },
- };
-});
+import { rolesAPIClientMock } from '../../../roles/roles_api_client.mock';
+import { coreMock } from '../../../../../../../../src/core/public/mocks';
describe('MappingInfoPanel', () => {
+ let rolesAPI: PublicMethodsOf;
+ beforeEach(() => {
+ rolesAPI = rolesAPIClientMock.create();
+ (rolesAPI as jest.Mocked).getRoles.mockResolvedValue([
+ { name: 'foo_role' },
+ { name: 'bar role' },
+ ] as Role[]);
+ });
+
it('renders when creating a role mapping, default to the "roles" view', () => {
const props = {
roleMapping: {
@@ -32,6 +38,8 @@ describe('MappingInfoPanel', () => {
metadata: {},
} as RoleMapping,
mode: 'create',
+ docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
+ rolesAPIClient: rolesAPI,
} as MappingInfoPanel['props'];
const wrapper = mountWithIntl();
@@ -77,6 +85,8 @@ describe('MappingInfoPanel', () => {
metadata: {},
} as RoleMapping,
mode: 'edit',
+ docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
+ rolesAPIClient: rolesAPI,
} as MappingInfoPanel['props'];
const wrapper = mountWithIntl();
@@ -101,6 +111,8 @@ describe('MappingInfoPanel', () => {
canUseInlineScripts: true,
canUseStoredScripts: false,
validateForm: false,
+ docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
+ rolesAPIClient: rolesAPI,
};
const wrapper = mountWithIntl();
@@ -140,6 +152,8 @@ describe('MappingInfoPanel', () => {
canUseInlineScripts: false,
canUseStoredScripts: true,
validateForm: false,
+ docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
+ rolesAPIClient: rolesAPI,
};
const wrapper = mountWithIntl();
@@ -179,6 +193,8 @@ describe('MappingInfoPanel', () => {
canUseInlineScripts: false,
canUseStoredScripts: false,
validateForm: false,
+ docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
+ rolesAPIClient: rolesAPI,
};
const wrapper = mountWithIntl();
@@ -202,6 +218,8 @@ describe('MappingInfoPanel', () => {
metadata: {},
} as RoleMapping,
mode: 'edit',
+ docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
+ rolesAPIClient: rolesAPI,
} as MappingInfoPanel['props'];
const wrapper = mountWithIntl();
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/mapping_info_panel/mapping_info_panel.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.tsx
similarity index 95%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/mapping_info_panel/mapping_info_panel.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.tsx
index a02b4fc1709f0..02af6bfbafa7e 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/mapping_info_panel/mapping_info_panel.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.tsx
@@ -18,14 +18,15 @@ import {
EuiSwitch,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
-import { RoleMapping } from '../../../../../../../common/model';
+import { RoleMapping } from '../../../../../common/model';
+import { RolesAPIClient } from '../../../roles';
import {
validateRoleMappingName,
validateRoleMappingRoles,
validateRoleMappingRoleTemplates,
-} from '../../services/role_mapping_validation';
+} from '../services/role_mapping_validation';
import { RoleSelector } from '../role_selector';
-import { documentationLinks } from '../../../services/documentation_links';
+import { DocumentationLinksService } from '../../documentation_links';
interface Props {
roleMapping: RoleMapping;
@@ -34,6 +35,8 @@ interface Props {
validateForm: boolean;
canUseInlineScripts: boolean;
canUseStoredScripts: boolean;
+ rolesAPIClient: PublicMethodsOf;
+ docLinks: DocumentationLinksService;
}
interface State {
@@ -163,6 +166,7 @@ export class MappingInfoPanel extends Component {
>
{
defaultMessage="Create templates that describe the roles to assign to your users."
/>{' '}
@@ -230,6 +234,7 @@ export class MappingInfoPanel extends Component {
>
{
- return {
- RolesApi: {
- getRoles: () => Promise.resolve([{ name: 'foo_role' }, { name: 'bar role' }]),
- },
- };
-});
+import { RolesAPIClient } from '../../../roles';
+import { rolesAPIClientMock } from '../../../roles/roles_api_client.mock';
describe('RoleSelector', () => {
+ let rolesAPI: PublicMethodsOf;
+ beforeEach(() => {
+ rolesAPI = rolesAPIClientMock.create();
+ (rolesAPI as jest.Mocked).getRoles.mockResolvedValue([
+ { name: 'foo_role' },
+ { name: 'bar role' },
+ ] as Role[]);
+ });
+
it('allows roles to be selected, removing any previously selected role templates', () => {
const props = {
roleMapping: {
@@ -36,6 +39,7 @@ describe('RoleSelector', () => {
canUseInlineScripts: true,
onChange: jest.fn(),
mode: 'roles',
+ rolesAPIClient: rolesAPI,
} as RoleSelector['props'];
const wrapper = mountWithIntl();
@@ -57,6 +61,7 @@ describe('RoleSelector', () => {
canUseInlineScripts: true,
onChange: jest.fn(),
mode: 'templates',
+ rolesAPIClient: rolesAPI,
} as RoleSelector['props'];
const wrapper = mountWithIntl();
@@ -87,6 +92,7 @@ describe('RoleSelector', () => {
canUseInlineScripts: true,
onChange: jest.fn(),
mode: 'templates',
+ rolesAPIClient: rolesAPI,
} as RoleSelector['props'];
const wrapper = mountWithIntl();
@@ -122,6 +128,7 @@ describe('RoleSelector', () => {
canUseInlineScripts: true,
onChange: jest.fn(),
mode: 'templates',
+ rolesAPIClient: rolesAPI,
} as RoleSelector['props'];
const wrapper = mountWithIntl();
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/role_selector/role_selector.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_selector.tsx
similarity index 94%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/role_selector/role_selector.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_selector.tsx
index 6b92d6b4907f1..992c2741ae93e 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/role_selector/role_selector.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_selector.tsx
@@ -7,12 +7,13 @@
import React, { Fragment } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiComboBox, EuiFormRow, EuiHorizontalRule } from '@elastic/eui';
-import { RoleMapping, Role } from '../../../../../../../common/model';
-import { RolesApi } from '../../../../../../lib/roles_api';
+import { RoleMapping, Role } from '../../../../../common/model';
+import { RolesAPIClient } from '../../../roles';
import { AddRoleTemplateButton } from './add_role_template_button';
import { RoleTemplateEditor } from './role_template_editor';
interface Props {
+ rolesAPIClient: PublicMethodsOf;
roleMapping: RoleMapping;
canUseInlineScripts: boolean;
canUseStoredScripts: boolean;
@@ -32,7 +33,7 @@ export class RoleSelector extends React.Component {
}
public async componentDidMount() {
- const roles = await RolesApi.getRoles();
+ const roles = await this.props.rolesAPIClient.getRoles();
this.setState({ roles });
}
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/role_selector/role_template_editor.test.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_template_editor.test.tsx
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/role_selector/role_template_editor.test.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_template_editor.test.tsx
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/role_selector/role_template_editor.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_template_editor.tsx
similarity index 98%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/role_selector/role_template_editor.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_template_editor.tsx
index 4b8d34d271996..d79651d7b9cd6 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/role_selector/role_template_editor.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_template_editor.tsx
@@ -18,12 +18,12 @@ import {
EuiSpacer,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import { RoleTemplate } from '../../../../../../../common/model';
+import { RoleTemplate } from '../../../../../common/model';
import {
isInlineRoleTemplate,
isStoredRoleTemplate,
isInvalidRoleTemplate,
-} from '../../services/role_template_type';
+} from '../services/role_template_type';
import { RoleTemplateTypeSelect } from './role_template_type_select';
interface Props {
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/role_selector/role_template_type_select.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_template_type_select.tsx
similarity index 92%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/role_selector/role_template_type_select.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_template_type_select.tsx
index 4a06af0fb436b..aa65c5c9bcae7 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/role_selector/role_template_type_select.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_template_type_select.tsx
@@ -7,8 +7,8 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
import { EuiComboBox } from '@elastic/eui';
-import { RoleTemplate } from '../../../../../../../common/model';
-import { isInlineRoleTemplate, isStoredRoleTemplate } from '../../services/role_template_type';
+import { RoleTemplate } from '../../../../../common/model';
+import { isInlineRoleTemplate, isStoredRoleTemplate } from '../services/role_template_type';
const templateTypeOptions = [
{
diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/_index.scss b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/_index.scss
new file mode 100644
index 0000000000000..c3b2764e64713
--- /dev/null
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/_index.scss
@@ -0,0 +1 @@
+@import './rule_editor_group';
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/_index.scss b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/_rule_editor_group.scss
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/_index.scss
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/_rule_editor_group.scss
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/add_rule_button.test.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/add_rule_button.test.tsx
similarity index 97%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/add_rule_button.test.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/add_rule_button.test.tsx
index 917b822acef3f..d1411bd9bf2b9 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/add_rule_button.test.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/add_rule_button.test.tsx
@@ -8,7 +8,7 @@ import React from 'react';
import { AddRuleButton } from './add_rule_button';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { findTestSubject } from 'test_utils/find_test_subject';
-import { FieldRule, AllRule } from '../../../model';
+import { FieldRule, AllRule } from '../../model';
describe('AddRuleButton', () => {
it('allows a field rule to be created', () => {
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/add_rule_button.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/add_rule_button.tsx
similarity index 97%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/add_rule_button.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/add_rule_button.tsx
index 100c0dd3eeaee..9696fa337a74f 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/add_rule_button.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/add_rule_button.tsx
@@ -7,7 +7,7 @@
import React, { useState } from 'react';
import { EuiButtonEmpty, EuiPopover, EuiContextMenuPanel, EuiContextMenuItem } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
-import { Rule, FieldRule, AllRule } from '../../../model';
+import { Rule, FieldRule, AllRule } from '../../model';
interface Props {
onClick: (newRule: Rule) => void;
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/field_rule_editor.test.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/field_rule_editor.test.tsx
similarity index 99%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/field_rule_editor.test.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/field_rule_editor.test.tsx
index 8d5d5c99ee99d..5374f4625336d 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/field_rule_editor.test.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/field_rule_editor.test.tsx
@@ -7,7 +7,7 @@
import React from 'react';
import { FieldRuleEditor } from './field_rule_editor';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
-import { FieldRule } from '../../../model';
+import { FieldRule } from '../../model';
import { findTestSubject } from 'test_utils/find_test_subject';
import { ReactWrapper } from 'enzyme';
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/field_rule_editor.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/field_rule_editor.tsx
similarity index 99%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/field_rule_editor.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/field_rule_editor.tsx
index 52cf70dbd12bd..2506c18dcaf3a 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/field_rule_editor.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/field_rule_editor.tsx
@@ -18,7 +18,7 @@ import {
EuiIcon,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import { FieldRule, FieldRuleValue } from '../../../model';
+import { FieldRule, FieldRuleValue } from '../../model';
interface Props {
rule: FieldRule;
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/index.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/index.tsx
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/index.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/index.tsx
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/json_rule_editor.test.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/json_rule_editor.test.tsx
similarity index 89%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/json_rule_editor.test.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/json_rule_editor.test.tsx
index 8a9b37ab0f406..263c456e901cd 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/json_rule_editor.test.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/json_rule_editor.test.tsx
@@ -17,7 +17,10 @@ import { act } from 'react-dom/test-utils';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { JSONRuleEditor } from './json_rule_editor';
import { EuiCodeEditor } from '@elastic/eui';
-import { AllRule, AnyRule, FieldRule, ExceptAnyRule, ExceptAllRule } from '../../../model';
+import { AllRule, AnyRule, FieldRule, ExceptAnyRule, ExceptAllRule } from '../../model';
+import { DocumentationLinksService } from '../../documentation_links';
+
+import { coreMock } from '../../../../../../../../src/core/public/mocks';
describe('JSONRuleEditor', () => {
it('renders an empty rule set', () => {
@@ -25,6 +28,7 @@ describe('JSONRuleEditor', () => {
rules: null,
onChange: jest.fn(),
onValidityChange: jest.fn(),
+ docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
};
const wrapper = mountWithIntl();
@@ -46,6 +50,7 @@ describe('JSONRuleEditor', () => {
]),
onChange: jest.fn(),
onValidityChange: jest.fn(),
+ docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
};
const wrapper = mountWithIntl();
@@ -79,6 +84,7 @@ describe('JSONRuleEditor', () => {
rules: null,
onChange: jest.fn(),
onValidityChange: jest.fn(),
+ docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
};
const wrapper = mountWithIntl();
@@ -100,6 +106,7 @@ describe('JSONRuleEditor', () => {
rules: null,
onChange: jest.fn(),
onValidityChange: jest.fn(),
+ docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
};
const wrapper = mountWithIntl();
@@ -130,6 +137,7 @@ describe('JSONRuleEditor', () => {
rules: null,
onChange: jest.fn(),
onValidityChange: jest.fn(),
+ docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
};
const wrapper = mountWithIntl();
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/json_rule_editor.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/json_rule_editor.tsx
similarity index 95%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/json_rule_editor.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/json_rule_editor.tsx
index 371fb59f7a5d1..e7a9149513d20 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/json_rule_editor.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/json_rule_editor.tsx
@@ -11,13 +11,14 @@ import 'brace/theme/github';
import { EuiCodeEditor, EuiFormRow, EuiButton, EuiSpacer, EuiLink, EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
-import { Rule, RuleBuilderError, generateRulesFromRaw } from '../../../model';
-import { documentationLinks } from '../../../services/documentation_links';
+import { DocumentationLinksService } from '../../documentation_links';
+import { Rule, RuleBuilderError, generateRulesFromRaw } from '../../model';
interface Props {
rules: Rule | null;
onChange: (updatedRules: Rule | null) => void;
onValidityChange: (isValid: boolean) => void;
+ docLinks: DocumentationLinksService;
}
export const JSONRuleEditor = (props: Props) => {
@@ -107,7 +108,7 @@ export const JSONRuleEditor = (props: Props) => {
values={{
roleMappingAPI: (
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/rule_editor_panel.test.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.test.tsx
similarity index 88%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/rule_editor_panel.test.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.test.tsx
index 809264183d30c..b9c650cc1f77a 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/rule_editor_panel.test.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.test.tsx
@@ -15,8 +15,11 @@ import { findTestSubject } from 'test_utils/find_test_subject';
// This is not required for the tests to pass, but it rather suppresses lengthy
// warnings in the console which adds unnecessary noise to the test output.
import 'test_utils/stub_web_worker';
-import { AllRule, FieldRule } from '../../../model';
+import { AllRule, FieldRule } from '../../model';
import { EuiErrorBoundary } from '@elastic/eui';
+import { DocumentationLinksService } from '../../documentation_links';
+
+import { coreMock } from '../../../../../../../../src/core/public/mocks';
describe('RuleEditorPanel', () => {
it('renders the visual editor when no rules are defined', () => {
@@ -25,6 +28,7 @@ describe('RuleEditorPanel', () => {
onChange: jest.fn(),
onValidityChange: jest.fn(),
validateForm: false,
+ docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
};
const wrapper = mountWithIntl();
expect(wrapper.find(VisualRuleEditor)).toHaveLength(1);
@@ -45,6 +49,7 @@ describe('RuleEditorPanel', () => {
onChange: jest.fn(),
onValidityChange: jest.fn(),
validateForm: false,
+ docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
};
const wrapper = mountWithIntl();
expect(wrapper.find(VisualRuleEditor)).toHaveLength(1);
@@ -68,6 +73,7 @@ describe('RuleEditorPanel', () => {
onChange: jest.fn(),
onValidityChange: jest.fn(),
validateForm: false,
+ docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
};
const wrapper = mountWithIntl();
@@ -103,6 +109,7 @@ describe('RuleEditorPanel', () => {
onChange: jest.fn(),
onValidityChange: jest.fn(),
validateForm: false,
+ docLinks: new DocumentationLinksService(coreMock.createStart().docLinks),
};
const wrapper = mountWithIntl();
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/rule_editor_panel.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.tsx
similarity index 94%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/rule_editor_panel.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.tsx
index 4aab49b2b2efc..6e6641caa1f39 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/rule_editor_panel.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.tsx
@@ -22,19 +22,20 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
-import { RoleMapping } from '../../../../../../../common/model';
+import { RoleMapping } from '../../../../../common/model';
import { VisualRuleEditor } from './visual_rule_editor';
import { JSONRuleEditor } from './json_rule_editor';
-import { VISUAL_MAX_RULE_DEPTH } from '../../services/role_mapping_constants';
-import { Rule, generateRulesFromRaw } from '../../../model';
-import { validateRoleMappingRules } from '../../services/role_mapping_validation';
-import { documentationLinks } from '../../../services/documentation_links';
+import { VISUAL_MAX_RULE_DEPTH } from '../services/role_mapping_constants';
+import { Rule, generateRulesFromRaw } from '../../model';
+import { DocumentationLinksService } from '../../documentation_links';
+import { validateRoleMappingRules } from '../services/role_mapping_validation';
interface Props {
rawRules: RoleMapping['rules'];
onChange: (rawRules: RoleMapping['rules']) => void;
onValidityChange: (isValid: boolean) => void;
validateForm: boolean;
+ docLinks: DocumentationLinksService;
}
interface State {
@@ -91,7 +92,7 @@ export class RuleEditorPanel extends Component {
values={{
learnMoreLink: (
@@ -214,6 +215,7 @@ export class RuleEditorPanel extends Component {
rules={this.state.rules}
onChange={this.onRuleChange}
onValidityChange={this.onValidityChange}
+ docLinks={this.props.docLinks}
/>
);
default:
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/rule_group_editor.test.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_group_editor.test.tsx
similarity index 99%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/rule_group_editor.test.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_group_editor.test.tsx
index 3e0e0e386e98c..5946aac4306b1 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/rule_group_editor.test.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_group_editor.test.tsx
@@ -7,7 +7,7 @@
import React from 'react';
import { RuleGroupEditor } from './rule_group_editor';
import { shallowWithIntl, mountWithIntl, nextTick } from 'test_utils/enzyme_helpers';
-import { AllRule, FieldRule, AnyRule, ExceptAnyRule } from '../../../model';
+import { AllRule, FieldRule, AnyRule, ExceptAnyRule } from '../../model';
import { FieldRuleEditor } from './field_rule_editor';
import { AddRuleButton } from './add_rule_button';
import { EuiContextMenuItem } from '@elastic/eui';
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/rule_group_editor.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_group_editor.tsx
similarity index 97%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/rule_group_editor.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_group_editor.tsx
index 6fb33db179e8a..c17a853a65467 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/rule_group_editor.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_group_editor.tsx
@@ -16,8 +16,8 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { AddRuleButton } from './add_rule_button';
import { RuleGroupTitle } from './rule_group_title';
import { FieldRuleEditor } from './field_rule_editor';
-import { RuleGroup, Rule, FieldRule } from '../../../model';
-import { isRuleGroup } from '../../services/is_rule_group';
+import { RuleGroup, Rule, FieldRule } from '../../model';
+import { isRuleGroup } from '../services/is_rule_group';
interface Props {
rule: RuleGroup;
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/rule_group_title.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_group_title.tsx
similarity index 97%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/rule_group_title.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_group_title.tsx
index e46893afd4d86..6bef9c09eeef3 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/rule_group_title.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_group_title.tsx
@@ -15,14 +15,7 @@ import {
EuiConfirmModal,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
-import {
- RuleGroup,
- AllRule,
- AnyRule,
- ExceptAllRule,
- ExceptAnyRule,
- FieldRule,
-} from '../../../model';
+import { RuleGroup, AllRule, AnyRule, ExceptAllRule, ExceptAnyRule, FieldRule } from '../../model';
interface Props {
rule: RuleGroup;
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/visual_rule_editor.test.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/visual_rule_editor.test.tsx
similarity index 99%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/visual_rule_editor.test.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/visual_rule_editor.test.tsx
index 7c63613ee1cc9..b40f0063acb08 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/visual_rule_editor.test.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/visual_rule_editor.test.tsx
@@ -8,7 +8,7 @@ import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { VisualRuleEditor } from './visual_rule_editor';
import { findTestSubject } from 'test_utils/find_test_subject';
-import { AnyRule, AllRule, FieldRule, ExceptAnyRule, ExceptAllRule } from '../../../model';
+import { AnyRule, AllRule, FieldRule, ExceptAnyRule, ExceptAllRule } from '../../model';
import { RuleGroupEditor } from './rule_group_editor';
import { FieldRuleEditor } from './field_rule_editor';
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/visual_rule_editor.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/visual_rule_editor.tsx
similarity index 95%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/visual_rule_editor.tsx
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/visual_rule_editor.tsx
index 214c583189fb8..2e3db275788ee 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/rule_editor_panel/visual_rule_editor.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/visual_rule_editor.tsx
@@ -9,9 +9,9 @@ import { EuiEmptyPrompt, EuiCallOut, EuiSpacer, EuiButton } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { FieldRuleEditor } from './field_rule_editor';
import { RuleGroupEditor } from './rule_group_editor';
-import { VISUAL_MAX_RULE_DEPTH } from '../../services/role_mapping_constants';
-import { Rule, FieldRule, RuleGroup, AllRule } from '../../../model';
-import { isRuleGroup } from '../../services/is_rule_group';
+import { VISUAL_MAX_RULE_DEPTH } from '../services/role_mapping_constants';
+import { Rule, FieldRule, RuleGroup, AllRule } from '../../model';
+import { isRuleGroup } from '../services/is_rule_group';
interface Props {
rules: Rule | null;
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/services/is_rule_group.ts b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/is_rule_group.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/services/is_rule_group.ts
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/is_rule_group.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/services/role_mapping_constants.ts b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_mapping_constants.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/services/role_mapping_constants.ts
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_mapping_constants.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/services/role_mapping_validation.test.ts b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_mapping_validation.test.ts
similarity index 98%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/services/role_mapping_validation.test.ts
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_mapping_validation.test.ts
index 9614c4338b631..0c3f988ae6b10 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/services/role_mapping_validation.test.ts
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_mapping_validation.test.ts
@@ -10,7 +10,7 @@ import {
validateRoleMappingRules,
validateRoleMappingForSave,
} from './role_mapping_validation';
-import { RoleMapping } from '../../../../../../common/model';
+import { RoleMapping } from '../../../../../common/model';
describe('validateRoleMappingName', () => {
it('requires a value', () => {
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/services/role_mapping_validation.ts b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_mapping_validation.ts
similarity index 97%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/services/role_mapping_validation.ts
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_mapping_validation.ts
index 5916d6fd9e189..7695f1da14881 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/services/role_mapping_validation.ts
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_mapping_validation.ts
@@ -5,7 +5,7 @@
*/
import { i18n } from '@kbn/i18n';
-import { RoleMapping } from '../../../../../../common/model';
+import { RoleMapping } from '../../../../../common/model';
import { generateRulesFromRaw } from '../../model';
interface ValidationResult {
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/services/role_template_type.test.ts b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_template_type.test.ts
similarity index 96%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/services/role_template_type.test.ts
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_template_type.test.ts
index 8e1f47a4157ae..c093bb1b3fbbc 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/services/role_template_type.test.ts
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_template_type.test.ts
@@ -9,7 +9,7 @@ import {
isInlineRoleTemplate,
isInvalidRoleTemplate,
} from './role_template_type';
-import { RoleTemplate } from '../../../../../../common/model';
+import { RoleTemplate } from '../../../../../common/model';
describe('#isStoredRoleTemplate', () => {
it('returns true for stored templates, false otherwise', () => {
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/services/role_template_type.ts b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_template_type.ts
similarity index 96%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/services/role_template_type.ts
rename to x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_template_type.ts
index 90d8d1a09e587..5e646535a6c4d 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/services/role_template_type.ts
+++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_template_type.ts
@@ -9,7 +9,7 @@ import {
StoredRoleTemplate,
InlineRoleTemplate,
InvalidRoleTemplate,
-} from '../../../../../../common/model';
+} from '../../../../../common/model';
export function isStoredRoleTemplate(
roleMappingTemplate: RoleTemplate
diff --git a/x-pack/plugins/security/public/management/role_mappings/index.mock.ts b/x-pack/plugins/security/public/management/role_mappings/index.mock.ts
new file mode 100644
index 0000000000000..826477a1a5b15
--- /dev/null
+++ b/x-pack/plugins/security/public/management/role_mappings/index.mock.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export { roleMappingsAPIClientMock } from './role_mappings_api_client.mock';
diff --git a/x-pack/plugins/security/public/management/role_mappings/index.ts b/x-pack/plugins/security/public/management/role_mappings/index.ts
new file mode 100644
index 0000000000000..f670ea6181038
--- /dev/null
+++ b/x-pack/plugins/security/public/management/role_mappings/index.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export { roleMappingsManagementApp } from './role_mappings_management_app';
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/__snapshots__/rule_builder.test.ts.snap b/x-pack/plugins/security/public/management/role_mappings/model/__snapshots__/rule_builder.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/model/__snapshots__/rule_builder.test.ts.snap
rename to x-pack/plugins/security/public/management/role_mappings/model/__snapshots__/rule_builder.test.ts.snap
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/all_rule.test.ts b/x-pack/plugins/security/public/management/role_mappings/model/all_rule.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/model/all_rule.test.ts
rename to x-pack/plugins/security/public/management/role_mappings/model/all_rule.test.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/all_rule.ts b/x-pack/plugins/security/public/management/role_mappings/model/all_rule.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/model/all_rule.ts
rename to x-pack/plugins/security/public/management/role_mappings/model/all_rule.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/any_rule.test.ts b/x-pack/plugins/security/public/management/role_mappings/model/any_rule.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/model/any_rule.test.ts
rename to x-pack/plugins/security/public/management/role_mappings/model/any_rule.test.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/any_rule.ts b/x-pack/plugins/security/public/management/role_mappings/model/any_rule.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/model/any_rule.ts
rename to x-pack/plugins/security/public/management/role_mappings/model/any_rule.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/except_all_rule.test.ts b/x-pack/plugins/security/public/management/role_mappings/model/except_all_rule.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/model/except_all_rule.test.ts
rename to x-pack/plugins/security/public/management/role_mappings/model/except_all_rule.test.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/except_all_rule.ts b/x-pack/plugins/security/public/management/role_mappings/model/except_all_rule.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/model/except_all_rule.ts
rename to x-pack/plugins/security/public/management/role_mappings/model/except_all_rule.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/except_any_rule.test.ts b/x-pack/plugins/security/public/management/role_mappings/model/except_any_rule.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/model/except_any_rule.test.ts
rename to x-pack/plugins/security/public/management/role_mappings/model/except_any_rule.test.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/except_any_rule.ts b/x-pack/plugins/security/public/management/role_mappings/model/except_any_rule.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/model/except_any_rule.ts
rename to x-pack/plugins/security/public/management/role_mappings/model/except_any_rule.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/field_rule.test.ts b/x-pack/plugins/security/public/management/role_mappings/model/field_rule.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/model/field_rule.test.ts
rename to x-pack/plugins/security/public/management/role_mappings/model/field_rule.test.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/field_rule.ts b/x-pack/plugins/security/public/management/role_mappings/model/field_rule.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/model/field_rule.ts
rename to x-pack/plugins/security/public/management/role_mappings/model/field_rule.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/index.ts b/x-pack/plugins/security/public/management/role_mappings/model/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/model/index.ts
rename to x-pack/plugins/security/public/management/role_mappings/model/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/rule.ts b/x-pack/plugins/security/public/management/role_mappings/model/rule.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/model/rule.ts
rename to x-pack/plugins/security/public/management/role_mappings/model/rule.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/rule_builder.test.ts b/x-pack/plugins/security/public/management/role_mappings/model/rule_builder.test.ts
similarity index 99%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/model/rule_builder.test.ts
rename to x-pack/plugins/security/public/management/role_mappings/model/rule_builder.test.ts
index ebd48f6d15d99..ad486a8b314c4 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/rule_builder.test.ts
+++ b/x-pack/plugins/security/public/management/role_mappings/model/rule_builder.test.ts
@@ -5,7 +5,7 @@
*/
import { generateRulesFromRaw, FieldRule } from '.';
-import { RoleMapping } from '../../../../../common/model';
+import { RoleMapping } from '../../../../common/model';
import { RuleBuilderError } from './rule_builder_error';
describe('generateRulesFromRaw', () => {
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/rule_builder.ts b/x-pack/plugins/security/public/management/role_mappings/model/rule_builder.ts
similarity index 99%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/model/rule_builder.ts
rename to x-pack/plugins/security/public/management/role_mappings/model/rule_builder.ts
index fe344b2ae38dd..a384e61e521ab 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/rule_builder.ts
+++ b/x-pack/plugins/security/public/management/role_mappings/model/rule_builder.ts
@@ -5,7 +5,7 @@
*/
import { i18n } from '@kbn/i18n';
-import { RoleMapping } from '../../../../../common/model';
+import { RoleMapping } from '../../../../common/model';
import { FieldRule, FieldRuleValue } from './field_rule';
import { AllRule } from './all_rule';
import { AnyRule } from './any_rule';
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/rule_builder_error.ts b/x-pack/plugins/security/public/management/role_mappings/model/rule_builder_error.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/model/rule_builder_error.ts
rename to x-pack/plugins/security/public/management/role_mappings/model/rule_builder_error.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/rule_group.ts b/x-pack/plugins/security/public/management/role_mappings/model/rule_group.ts
similarity index 94%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/model/rule_group.ts
rename to x-pack/plugins/security/public/management/role_mappings/model/rule_group.ts
index 3e1e7fad9b36f..5077c79a543c4 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/model/rule_group.ts
+++ b/x-pack/plugins/security/public/management/role_mappings/model/rule_group.ts
@@ -7,7 +7,7 @@
import { Rule } from './rule';
/**
- * Represents a catagory of Role Mapping rules which are capable of containing other rules.
+ * Represents a category of Role Mapping rules which are capable of containing other rules.
*/
export abstract class RuleGroup extends Rule {
/**
diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.mock.ts b/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.mock.ts
new file mode 100644
index 0000000000000..07d583d1e983f
--- /dev/null
+++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.mock.ts
@@ -0,0 +1,15 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export const roleMappingsAPIClientMock = {
+ create: () => ({
+ checkRoleMappingFeatures: jest.fn(),
+ getRoleMappings: jest.fn(),
+ getRoleMapping: jest.fn(),
+ saveRoleMapping: jest.fn(),
+ deleteRoleMappings: jest.fn(),
+ }),
+};
diff --git a/x-pack/legacy/plugins/security/public/lib/role_mappings_api.ts b/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts
similarity index 89%
rename from x-pack/legacy/plugins/security/public/lib/role_mappings_api.ts
rename to x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts
index b8bcba91388b5..0a88ed1da9ac3 100644
--- a/x-pack/legacy/plugins/security/public/lib/role_mappings_api.ts
+++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts
@@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { CoreSetup } from 'src/core/public';
-import { RoleMapping } from '../../common/model';
+import { HttpStart } from 'src/core/public';
+import { RoleMapping } from '../../../common/model';
interface CheckRoleMappingFeaturesResponse {
canManageRoleMappings: boolean;
@@ -20,8 +20,8 @@ type DeleteRoleMappingsResponse = Array<{
error?: Error;
}>;
-export class RoleMappingsAPI {
- constructor(private readonly http: CoreSetup['http']) {}
+export class RoleMappingsAPIClient {
+ constructor(private readonly http: HttpStart) {}
public async checkRoleMappingFeatures(): Promise {
return this.http.get(`/internal/security/_check_role_mapping_features`);
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/create_role_mapping_button/create_role_mapping_button.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/create_role_mapping_button/create_role_mapping_button.tsx
similarity index 90%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/create_role_mapping_button/create_role_mapping_button.tsx
rename to x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/create_role_mapping_button/create_role_mapping_button.tsx
index 2342eeb97d03e..6fe4bcc7a0bbb 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/create_role_mapping_button/create_role_mapping_button.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/create_role_mapping_button/create_role_mapping_button.tsx
@@ -7,7 +7,7 @@
import React from 'react';
import { EuiButton } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
-import { getCreateRoleMappingHref } from '../../../../management_urls';
+import { getCreateRoleMappingHref } from '../../../management_urls';
export const CreateRoleMappingButton = () => {
return (
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/create_role_mapping_button/index.ts b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/create_role_mapping_button/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/create_role_mapping_button/index.ts
rename to x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/create_role_mapping_button/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/empty_prompt/empty_prompt.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/empty_prompt/empty_prompt.tsx
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/empty_prompt/empty_prompt.tsx
rename to x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/empty_prompt/empty_prompt.tsx
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/empty_prompt/index.ts b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/empty_prompt/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/empty_prompt/index.ts
rename to x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/empty_prompt/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/index.ts b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/index.ts
rename to x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/index.ts
diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.test.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.test.tsx
new file mode 100644
index 0000000000000..de0722b4cd85e
--- /dev/null
+++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.test.tsx
@@ -0,0 +1,219 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers';
+import { RoleMappingsGridPage } from '.';
+import { SectionLoading, PermissionDenied, NoCompatibleRealms } from '../components';
+import { EmptyPrompt } from './empty_prompt';
+import { findTestSubject } from 'test_utils/find_test_subject';
+import { EuiLink } from '@elastic/eui';
+import { act } from '@testing-library/react';
+import { DocumentationLinksService } from '../documentation_links';
+
+import { coreMock } from '../../../../../../../src/core/public/mocks';
+import { roleMappingsAPIClientMock } from '../role_mappings_api_client.mock';
+
+describe('RoleMappingsGridPage', () => {
+ it('renders an empty prompt when no role mappings exist', async () => {
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.getRoleMappings.mockResolvedValue([]);
+ roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({
+ canManageRoleMappings: true,
+ hasCompatibleRealms: true,
+ });
+
+ const { docLinks, notifications } = coreMock.createStart();
+ const wrapper = mountWithIntl(
+
+ );
+ expect(wrapper.find(SectionLoading)).toHaveLength(1);
+ expect(wrapper.find(EmptyPrompt)).toHaveLength(0);
+
+ await nextTick();
+ wrapper.update();
+
+ expect(wrapper.find(SectionLoading)).toHaveLength(0);
+ expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0);
+ expect(wrapper.find(EmptyPrompt)).toHaveLength(1);
+ });
+
+ it('renders a permission denied message when unauthorized to manage role mappings', async () => {
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({
+ canManageRoleMappings: false,
+ hasCompatibleRealms: true,
+ });
+
+ const { docLinks, notifications } = coreMock.createStart();
+ const wrapper = mountWithIntl(
+
+ );
+ expect(wrapper.find(SectionLoading)).toHaveLength(1);
+ expect(wrapper.find(PermissionDenied)).toHaveLength(0);
+
+ await nextTick();
+ wrapper.update();
+
+ expect(wrapper.find(SectionLoading)).toHaveLength(0);
+ expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0);
+ expect(wrapper.find(PermissionDenied)).toHaveLength(1);
+ });
+
+ it('renders a warning when there are no compatible realms enabled', async () => {
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.getRoleMappings.mockResolvedValue([
+ {
+ name: 'some realm',
+ enabled: true,
+ roles: [],
+ rules: { field: { username: '*' } },
+ },
+ ]);
+ roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({
+ canManageRoleMappings: true,
+ hasCompatibleRealms: false,
+ });
+
+ const { docLinks, notifications } = coreMock.createStart();
+ const wrapper = mountWithIntl(
+
+ );
+ expect(wrapper.find(SectionLoading)).toHaveLength(1);
+ expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0);
+
+ await nextTick();
+ wrapper.update();
+
+ expect(wrapper.find(SectionLoading)).toHaveLength(0);
+ expect(wrapper.find(NoCompatibleRealms)).toHaveLength(1);
+ });
+
+ it('renders links to mapped roles', async () => {
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.getRoleMappings.mockResolvedValue([
+ {
+ name: 'some realm',
+ enabled: true,
+ roles: ['superuser'],
+ rules: { field: { username: '*' } },
+ },
+ ]);
+ roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({
+ canManageRoleMappings: true,
+ hasCompatibleRealms: true,
+ });
+
+ const { docLinks, notifications } = coreMock.createStart();
+ const wrapper = mountWithIntl(
+
+ );
+ await nextTick();
+ wrapper.update();
+
+ const links = findTestSubject(wrapper, 'roleMappingRoles').find(EuiLink);
+ expect(links).toHaveLength(1);
+ expect(links.at(0).props()).toMatchObject({
+ href: '#/management/security/roles/edit/superuser',
+ });
+ });
+
+ it('describes the number of mapped role templates', async () => {
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.getRoleMappings.mockResolvedValue([
+ {
+ name: 'some realm',
+ enabled: true,
+ role_templates: [{}, {}],
+ rules: { field: { username: '*' } },
+ },
+ ]);
+ roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({
+ canManageRoleMappings: true,
+ hasCompatibleRealms: true,
+ });
+
+ const { docLinks, notifications } = coreMock.createStart();
+ const wrapper = mountWithIntl(
+
+ );
+ await nextTick();
+ wrapper.update();
+
+ const templates = findTestSubject(wrapper, 'roleMappingRoles');
+ expect(templates).toHaveLength(1);
+ expect(templates.text()).toEqual(`2 role templates defined`);
+ });
+
+ it('allows role mappings to be deleted, refreshing the grid after', async () => {
+ const roleMappingsAPI = roleMappingsAPIClientMock.create();
+ roleMappingsAPI.getRoleMappings.mockResolvedValue([
+ {
+ name: 'some-realm',
+ enabled: true,
+ roles: ['superuser'],
+ rules: { field: { username: '*' } },
+ },
+ ]);
+ roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({
+ canManageRoleMappings: true,
+ hasCompatibleRealms: true,
+ });
+ roleMappingsAPI.deleteRoleMappings.mockResolvedValue([
+ {
+ name: 'some-realm',
+ success: true,
+ },
+ ]);
+
+ const { docLinks, notifications } = coreMock.createStart();
+ const wrapper = mountWithIntl(
+
+ );
+ await nextTick();
+ wrapper.update();
+
+ expect(roleMappingsAPI.getRoleMappings).toHaveBeenCalledTimes(1);
+ expect(roleMappingsAPI.deleteRoleMappings).not.toHaveBeenCalled();
+
+ findTestSubject(wrapper, `deleteRoleMappingButton-some-realm`).simulate('click');
+ expect(findTestSubject(wrapper, 'deleteRoleMappingConfirmationModal')).toHaveLength(1);
+
+ await act(async () => {
+ findTestSubject(wrapper, 'confirmModalConfirmButton').simulate('click');
+ await nextTick();
+ wrapper.update();
+ });
+
+ expect(roleMappingsAPI.deleteRoleMappings).toHaveBeenCalledWith(['some-realm']);
+ // Expect an additional API call to refresh the grid
+ expect(roleMappingsAPI.getRoleMappings).toHaveBeenCalledTimes(2);
+ });
+});
diff --git a/x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/role_mappings_grid_page.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx
similarity index 93%
rename from x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/role_mappings_grid_page.tsx
rename to x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx
index 7b23f2288d1ba..feb918cb6b301 100644
--- a/x-pack/legacy/plugins/security/public/views/management/role_mappings/role_mappings_grid/components/role_mappings_grid_page.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx
@@ -25,24 +25,27 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
-import { RoleMapping } from '../../../../../../common/model';
-import { RoleMappingsAPI } from '../../../../../lib/role_mappings_api';
+import { NotificationsStart } from 'src/core/public';
+import { RoleMapping } from '../../../../common/model';
import { EmptyPrompt } from './empty_prompt';
import {
NoCompatibleRealms,
DeleteProvider,
PermissionDenied,
SectionLoading,
-} from '../../components';
-import { documentationLinks } from '../../services/documentation_links';
+} from '../components';
import {
getCreateRoleMappingHref,
getEditRoleMappingHref,
getEditRoleHref,
-} from '../../../management_urls';
+} from '../../management_urls';
+import { DocumentationLinksService } from '../documentation_links';
+import { RoleMappingsAPIClient } from '../role_mappings_api_client';
interface Props {
- roleMappingsAPI: RoleMappingsAPI;
+ roleMappingsAPI: PublicMethodsOf;
+ notifications: NotificationsStart;
+ docLinks: DocumentationLinksService;
}
interface State {
@@ -140,7 +143,7 @@ export class RoleMappingsGridPage extends Component {
values={{
learnMoreLink: (
@@ -168,7 +171,7 @@ export class RoleMappingsGridPage extends Component {
{!this.state.hasCompatibleRealms && (
<>
-
+
>
)}
@@ -214,7 +217,10 @@ export class RoleMappingsGridPage extends Component {
const search = {
toolsLeft: selectedItems.length ? (
-
+
{deleteRoleMappingsPrompt => {
return (
{
return (
-
+
{deleteRoleMappingPrompt => {
return (
({
+ RoleMappingsGridPage: (props: any) => `Role Mappings Page: ${JSON.stringify(props)}`,
+}));
+
+jest.mock('./edit_role_mapping', () => ({
+ EditRoleMappingPage: (props: any) => `Role Mapping Edit Page: ${JSON.stringify(props)}`,
+}));
+
+import { roleMappingsManagementApp } from './role_mappings_management_app';
+
+import { coreMock } from '../../../../../../src/core/public/mocks';
+
+async function mountApp(basePath: string) {
+ const container = document.createElement('div');
+ const setBreadcrumbs = jest.fn();
+
+ const unmount = await roleMappingsManagementApp
+ .create({ getStartServices: coreMock.createSetup().getStartServices as any })
+ .mount({ basePath, element: container, setBreadcrumbs });
+
+ return { unmount, container, setBreadcrumbs };
+}
+
+describe('roleMappingsManagementApp', () => {
+ it('create() returns proper management app descriptor', () => {
+ expect(
+ roleMappingsManagementApp.create({
+ getStartServices: coreMock.createSetup().getStartServices as any,
+ })
+ ).toMatchInlineSnapshot(`
+ Object {
+ "id": "role_mappings",
+ "mount": [Function],
+ "order": 40,
+ "title": "Role Mappings",
+ }
+ `);
+ });
+
+ it('mount() works for the `grid` page', async () => {
+ const basePath = '/some-base-path/role_mappings';
+ window.location.hash = basePath;
+
+ const { setBreadcrumbs, container, unmount } = await mountApp(basePath);
+
+ expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
+ expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `#${basePath}`, text: 'Role Mappings' }]);
+ expect(container).toMatchInlineSnapshot(`
+
+ Role Mappings Page: {"notifications":{"toasts":{}},"roleMappingsAPI":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"}}
+
+ `);
+
+ unmount();
+
+ expect(container).toMatchInlineSnapshot(``);
+ });
+
+ it('mount() works for the `create role mapping` page', async () => {
+ const basePath = '/some-base-path/role_mappings';
+ window.location.hash = `${basePath}/edit`;
+
+ const { setBreadcrumbs, container, unmount } = await mountApp(basePath);
+
+ expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
+ expect(setBreadcrumbs).toHaveBeenCalledWith([
+ { href: `#${basePath}`, text: 'Role Mappings' },
+ { text: 'Create' },
+ ]);
+ expect(container).toMatchInlineSnapshot(`
+
+ Role Mapping Edit Page: {"roleMappingsAPI":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"notifications":{"toasts":{}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"}}
+
+ `);
+
+ unmount();
+
+ expect(container).toMatchInlineSnapshot(``);
+ });
+
+ it('mount() works for the `edit role mapping` page', async () => {
+ const basePath = '/some-base-path/role_mappings';
+ const roleMappingName = 'someRoleMappingName';
+ window.location.hash = `${basePath}/edit/${roleMappingName}`;
+
+ const { setBreadcrumbs, container, unmount } = await mountApp(basePath);
+
+ expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
+ expect(setBreadcrumbs).toHaveBeenCalledWith([
+ { href: `#${basePath}`, text: 'Role Mappings' },
+ { href: `#/some-base-path/role_mappings/edit/${roleMappingName}`, text: roleMappingName },
+ ]);
+ expect(container).toMatchInlineSnapshot(`
+
+ Role Mapping Edit Page: {"name":"someRoleMappingName","roleMappingsAPI":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"notifications":{"toasts":{}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"}}
+
+ `);
+
+ unmount();
+
+ expect(container).toMatchInlineSnapshot(``);
+ });
+
+ it('mount() properly encodes role mapping name in `edit role mapping` page link in breadcrumbs', async () => {
+ const basePath = '/some-base-path/role_mappings';
+ const roleMappingName = 'some 安全性 role mapping';
+ window.location.hash = `${basePath}/edit/${roleMappingName}`;
+
+ const { setBreadcrumbs } = await mountApp(basePath);
+
+ expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
+ expect(setBreadcrumbs).toHaveBeenCalledWith([
+ { href: `#${basePath}`, text: 'Role Mappings' },
+ {
+ href:
+ '#/some-base-path/role_mappings/edit/some%20%E5%AE%89%E5%85%A8%E6%80%A7%20role%20mapping',
+ text: roleMappingName,
+ },
+ ]);
+ });
+});
diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx
new file mode 100644
index 0000000000000..af1572cedbade
--- /dev/null
+++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx
@@ -0,0 +1,104 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { render, unmountComponentAtNode } from 'react-dom';
+import { HashRouter as Router, Route, Switch, useParams } from 'react-router-dom';
+import { i18n } from '@kbn/i18n';
+import { CoreSetup } from 'src/core/public';
+import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public';
+import { PluginStartDependencies } from '../../plugin';
+import { RolesAPIClient } from '../roles';
+import { RoleMappingsAPIClient } from './role_mappings_api_client';
+import { DocumentationLinksService } from './documentation_links';
+import { RoleMappingsGridPage } from './role_mappings_grid';
+import { EditRoleMappingPage } from './edit_role_mapping';
+
+interface CreateParams {
+ getStartServices: CoreSetup['getStartServices'];
+}
+
+export const roleMappingsManagementApp = Object.freeze({
+ id: 'role_mappings',
+ create({ getStartServices }: CreateParams) {
+ return {
+ id: this.id,
+ order: 40,
+ title: i18n.translate('xpack.security.management.roleMappingsTitle', {
+ defaultMessage: 'Role Mappings',
+ }),
+ async mount({ basePath, element, setBreadcrumbs }) {
+ const [{ docLinks, http, notifications, i18n: i18nStart }] = await getStartServices();
+ const roleMappingsBreadcrumbs = [
+ {
+ text: i18n.translate('xpack.security.roleMapping.breadcrumb', {
+ defaultMessage: 'Role Mappings',
+ }),
+ href: `#${basePath}`,
+ },
+ ];
+
+ const roleMappingsAPIClient = new RoleMappingsAPIClient(http);
+ const dockLinksService = new DocumentationLinksService(docLinks);
+ const RoleMappingsGridPageWithBreadcrumbs = () => {
+ setBreadcrumbs(roleMappingsBreadcrumbs);
+ return (
+
+ );
+ };
+
+ const EditRoleMappingsPageWithBreadcrumbs = () => {
+ const { name } = useParams<{ name?: string }>();
+
+ setBreadcrumbs([
+ ...roleMappingsBreadcrumbs,
+ name
+ ? { text: name, href: `#${basePath}/edit/${encodeURIComponent(name)}` }
+ : {
+ text: i18n.translate('xpack.security.roleMappings.createBreadcrumb', {
+ defaultMessage: 'Create',
+ }),
+ },
+ ]);
+
+ return (
+
+ );
+ };
+
+ render(
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ element
+ );
+
+ return () => {
+ unmountComponentAtNode(element);
+ };
+ },
+ } as RegisterManagementAppArgs;
+ },
+});
diff --git a/x-pack/plugins/security/public/management/roles/_index.scss b/x-pack/plugins/security/public/management/roles/_index.scss
new file mode 100644
index 0000000000000..5256c79f01f10
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/_index.scss
@@ -0,0 +1 @@
+@import './edit_role/index';
diff --git a/x-pack/plugins/security/public/management/roles/documentation_links.ts b/x-pack/plugins/security/public/management/roles/documentation_links.ts
new file mode 100644
index 0000000000000..cf46973d3541c
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/documentation_links.ts
@@ -0,0 +1,27 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { DocLinksStart } from 'src/core/public';
+
+export class DocumentationLinksService {
+ private readonly esDocBasePath: string;
+
+ constructor(docLinks: DocLinksStart) {
+ this.esDocBasePath = `${docLinks.ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${docLinks.DOC_LINK_VERSION}/`;
+ }
+
+ public getESClusterPrivilegesDocUrl() {
+ return `${this.esDocBasePath}security-privileges.html#privileges-list-cluster`;
+ }
+
+ public getESRunAsPrivilegesDocUrl() {
+ return `${this.esDocBasePath}security-privileges.html#_run_as_privilege`;
+ }
+
+ public getESIndicesPrivilegesDocUrl() {
+ return `${this.esDocBasePath}security-privileges.html#privileges-list-indices`;
+ }
+}
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/lib/__snapshots__/validate_role.test.ts.snap b/x-pack/plugins/security/public/management/roles/edit_role/__snapshots__/validate_role.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/lib/__snapshots__/validate_role.test.ts.snap
rename to x-pack/plugins/security/public/management/roles/edit_role/__snapshots__/validate_role.test.ts.snap
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/_index.scss b/x-pack/plugins/security/public/management/roles/edit_role/_index.scss
new file mode 100644
index 0000000000000..0153b1734ceba
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/edit_role/_index.scss
@@ -0,0 +1,3 @@
+@import './collapsible_panel/index';
+@import './spaces_popover_list/index';
+@import './privileges/index';
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/collapsible_panel/__snapshots__/collapsible_panel.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/__snapshots__/collapsible_panel.test.tsx.snap
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/collapsible_panel/__snapshots__/collapsible_panel.test.tsx.snap
rename to x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/__snapshots__/collapsible_panel.test.tsx.snap
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/collapsible_panel/_collapsible_panel.scss b/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/_collapsible_panel.scss
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/collapsible_panel/_collapsible_panel.scss
rename to x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/_collapsible_panel.scss
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/_index.scss b/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/_index.scss
new file mode 100644
index 0000000000000..c0f4f8ab9a870
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/_index.scss
@@ -0,0 +1 @@
+@import './collapsible_panel';
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/collapsible_panel/collapsible_panel.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/collapsible_panel.test.tsx
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/collapsible_panel/collapsible_panel.test.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/collapsible_panel.test.tsx
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/collapsible_panel/collapsible_panel.tsx b/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/collapsible_panel.tsx
similarity index 99%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/collapsible_panel/collapsible_panel.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/collapsible_panel.tsx
index 416dd7f6c4e5c..01af7cb4509f6 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/collapsible_panel/collapsible_panel.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/collapsible_panel.tsx
@@ -50,7 +50,6 @@ export class CollapsiblePanel extends Component {
public getTitle = () => {
return (
- // @ts-ignore
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/collapsible_panel/index.ts b/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/collapsible_panel/index.ts
rename to x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/delete_role_button.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/delete_role_button.test.tsx
similarity index 94%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/delete_role_button.test.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/delete_role_button.test.tsx
index cc16866c88355..f4af935be6648 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/delete_role_button.test.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/delete_role_button.test.tsx
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- EuiButtonEmpty,
- // @ts-ignore
- EuiConfirmModal,
-} from '@elastic/eui';
+import { EuiButtonEmpty, EuiConfirmModal } from '@elastic/eui';
import React from 'react';
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
import { DeleteRoleButton } from './delete_role_button';
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/delete_role_button.tsx b/x-pack/plugins/security/public/management/roles/edit_role/delete_role_button.tsx
similarity index 95%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/delete_role_button.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/delete_role_button.tsx
index 1ae84d3fb7224..c6a10396f235c 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/delete_role_button.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/delete_role_button.tsx
@@ -4,13 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- EuiButtonEmpty,
- // @ts-ignore
- EuiConfirmModal,
- // @ts-ignore
- EuiOverlayMask,
-} from '@elastic/eui';
+import { EuiButtonEmpty, EuiConfirmModal, EuiOverlayMask } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { Component, Fragment } from 'react';
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx
new file mode 100644
index 0000000000000..2b3d7c811f6de
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx
@@ -0,0 +1,552 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { ReactWrapper } from 'enzyme';
+import React from 'react';
+import { act } from '@testing-library/react';
+import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers';
+import { Capabilities } from 'src/core/public';
+import { Space } from '../../../../../spaces/common/model/space';
+import { Feature } from '../../../../../features/public';
+// These modules should be moved into a common directory
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { Actions } from '../../../../server/authorization/actions';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { privilegesFactory } from '../../../../server/authorization/privileges';
+import { Role } from '../../../../common/model';
+import { DocumentationLinksService } from '../documentation_links';
+import { EditRolePage } from './edit_role_page';
+import { SimplePrivilegeSection } from './privileges/kibana/simple_privilege_section';
+import { SpaceAwarePrivilegeSection } from './privileges/kibana/space_aware_privilege_section';
+
+import { TransformErrorSection } from './privileges/kibana/transform_error_section';
+import { coreMock } from '../../../../../../../src/core/public/mocks';
+import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks';
+import { licenseMock } from '../../../../common/licensing/index.mock';
+import { userAPIClientMock } from '../../users/index.mock';
+import { rolesAPIClientMock, indicesAPIClientMock, privilegesAPIClientMock } from '../index.mock';
+
+const buildFeatures = () => {
+ return [
+ {
+ id: 'feature1',
+ name: 'Feature 1',
+ icon: 'addDataApp',
+ app: ['feature1App'],
+ privileges: {
+ all: {
+ app: ['feature1App'],
+ ui: ['feature1-ui'],
+ savedObject: {
+ all: [],
+ read: [],
+ },
+ },
+ },
+ },
+ {
+ id: 'feature2',
+ name: 'Feature 2',
+ icon: 'addDataApp',
+ app: ['feature2App'],
+ privileges: {
+ all: {
+ app: ['feature2App'],
+ ui: ['feature2-ui'],
+ savedObject: {
+ all: ['feature2'],
+ read: ['config'],
+ },
+ },
+ },
+ },
+ ] as Feature[];
+};
+
+const buildRawKibanaPrivileges = () => {
+ return privilegesFactory(new Actions('unit_test_version'), {
+ getFeatures: () => buildFeatures(),
+ }).get();
+};
+
+const buildBuiltinESPrivileges = () => {
+ return {
+ cluster: ['all', 'manage', 'monitor'],
+ index: ['all', 'read', 'write', 'index'],
+ };
+};
+
+const buildUICapabilities = (canManageSpaces = true) => {
+ return {
+ catalogue: {},
+ management: {},
+ navLinks: {},
+ spaces: {
+ manage: canManageSpaces,
+ },
+ } as Capabilities;
+};
+
+const buildSpaces = () => {
+ return [
+ {
+ id: 'default',
+ name: 'Default',
+ disabledFeatures: [],
+ _reserved: true,
+ },
+ {
+ id: 'space_1',
+ name: 'Space 1',
+ disabledFeatures: [],
+ },
+ {
+ id: 'space_2',
+ name: 'Space 2',
+ disabledFeatures: ['feature2'],
+ },
+ ] as Space[];
+};
+
+const expectReadOnlyFormButtons = (wrapper: ReactWrapper) => {
+ expect(wrapper.find('button[data-test-subj="roleFormReturnButton"]')).toHaveLength(1);
+ expect(wrapper.find('button[data-test-subj="roleFormSaveButton"]')).toHaveLength(0);
+};
+
+const expectSaveFormButtons = (wrapper: ReactWrapper) => {
+ expect(wrapper.find('button[data-test-subj="roleFormReturnButton"]')).toHaveLength(0);
+ expect(wrapper.find('button[data-test-subj="roleFormSaveButton"]')).toHaveLength(1);
+};
+
+function getProps({
+ action,
+ role,
+ canManageSpaces = true,
+ spacesEnabled = true,
+}: {
+ action: 'edit' | 'clone';
+ role?: Role;
+ canManageSpaces?: boolean;
+ spacesEnabled?: boolean;
+}) {
+ const rolesAPIClient = rolesAPIClientMock.create();
+ rolesAPIClient.getRole.mockResolvedValue(role);
+
+ const indexPatterns = dataPluginMock.createStartContract().indexPatterns;
+ indexPatterns.getTitles = jest.fn().mockResolvedValue(['foo*', 'bar*']);
+
+ const indicesAPIClient = indicesAPIClientMock.create();
+
+ const userAPIClient = userAPIClientMock.create();
+ userAPIClient.getUsers.mockResolvedValue([]);
+
+ const privilegesAPIClient = privilegesAPIClientMock.create();
+ privilegesAPIClient.getAll.mockResolvedValue(buildRawKibanaPrivileges());
+ privilegesAPIClient.getBuiltIn.mockResolvedValue(buildBuiltinESPrivileges());
+
+ const license = licenseMock.create();
+ license.getFeatures.mockReturnValue({
+ allowRoleDocumentLevelSecurity: true,
+ allowRoleFieldLevelSecurity: true,
+ } as any);
+
+ const { fatalErrors } = coreMock.createSetup();
+ const { http, docLinks, notifications } = coreMock.createStart();
+ http.get.mockImplementation(async path => {
+ if (path === '/api/features') {
+ return buildFeatures();
+ }
+
+ if (path === '/api/spaces/space') {
+ return buildSpaces();
+ }
+ });
+
+ return {
+ action,
+ roleName: role?.name,
+ license,
+ http,
+ indexPatterns,
+ indicesAPIClient,
+ privilegesAPIClient,
+ rolesAPIClient,
+ userAPIClient,
+ notifications,
+ docLinks: new DocumentationLinksService(docLinks),
+ fatalErrors,
+ spacesEnabled,
+ uiCapabilities: buildUICapabilities(canManageSpaces),
+ };
+}
+
+describe('', () => {
+ describe('with spaces enabled', () => {
+ it('can render a reserved role', async () => {
+ const wrapper = mountWithIntl(
+
+ );
+
+ await act(async () => {
+ await nextTick();
+ wrapper.update();
+ });
+
+ expect(wrapper.find('[data-test-subj="reservedRoleBadgeTooltip"]')).toHaveLength(1);
+ expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1);
+ expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0);
+ expectReadOnlyFormButtons(wrapper);
+ });
+
+ it('can render a user defined role', async () => {
+ const wrapper = mountWithIntl(
+
+ );
+
+ await act(async () => {
+ await nextTick();
+ wrapper.update();
+ });
+
+ expect(wrapper.find('[data-test-subj="reservedRoleBadgeTooltip"]')).toHaveLength(0);
+ expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1);
+ expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0);
+ expectSaveFormButtons(wrapper);
+ });
+
+ it('can render when creating a new role', async () => {
+ const wrapper = mountWithIntl();
+
+ await act(async () => {
+ await nextTick();
+ wrapper.update();
+ });
+
+ expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1);
+ expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0);
+ expectSaveFormButtons(wrapper);
+ });
+
+ it('can render when cloning an existing role', async () => {
+ const wrapper = mountWithIntl(
+
+ );
+
+ await act(async () => {
+ await nextTick();
+ wrapper.update();
+ });
+
+ expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1);
+ expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0);
+ expectSaveFormButtons(wrapper);
+ });
+
+ it('renders an auth error when not authorized to manage spaces', async () => {
+ const wrapper = mountWithIntl(
+
+ );
+
+ await act(async () => {
+ await nextTick();
+ wrapper.update();
+ });
+
+ expect(wrapper.find('[data-test-subj="reservedRoleBadgeTooltip"]')).toHaveLength(0);
+
+ expect(
+ wrapper.find('EuiCallOut[data-test-subj="userCannotManageSpacesCallout"]')
+ ).toHaveLength(1);
+
+ expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1);
+ expectSaveFormButtons(wrapper);
+ });
+
+ it('renders a partial read-only view when there is a transform error', async () => {
+ const wrapper = mountWithIntl(
+
+ );
+
+ await act(async () => {
+ await nextTick();
+ wrapper.update();
+ });
+
+ expect(wrapper.find(TransformErrorSection)).toHaveLength(1);
+ expectReadOnlyFormButtons(wrapper);
+ });
+ });
+
+ describe('with spaces disabled', () => {
+ it('can render a reserved role', async () => {
+ const wrapper = mountWithIntl(
+
+ );
+
+ await act(async () => {
+ await nextTick();
+ wrapper.update();
+ });
+
+ expect(wrapper.find('[data-test-subj="reservedRoleBadgeTooltip"]')).toHaveLength(1);
+ expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1);
+ expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0);
+ expectReadOnlyFormButtons(wrapper);
+ });
+
+ it('can render a user defined role', async () => {
+ const wrapper = mountWithIntl(
+
+ );
+
+ await act(async () => {
+ await nextTick();
+ wrapper.update();
+ });
+
+ expect(wrapper.find('[data-test-subj="reservedRoleBadgeTooltip"]')).toHaveLength(0);
+ expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1);
+ expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0);
+ expectSaveFormButtons(wrapper);
+ });
+
+ it('can render when creating a new role', async () => {
+ const wrapper = mountWithIntl(
+
+ );
+
+ await act(async () => {
+ await nextTick();
+ wrapper.update();
+ });
+
+ expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1);
+ expectSaveFormButtons(wrapper);
+ });
+
+ it('can render when cloning an existing role', async () => {
+ const wrapper = mountWithIntl(
+
+ );
+
+ await act(async () => {
+ await nextTick();
+ wrapper.update();
+ });
+
+ expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1);
+ expectSaveFormButtons(wrapper);
+ });
+
+ it('does not care if user cannot manage spaces', async () => {
+ const wrapper = mountWithIntl(
+
+ );
+
+ await act(async () => {
+ await nextTick();
+ wrapper.update();
+ });
+
+ expect(wrapper.find('[data-test-subj="reservedRoleBadgeTooltip"]')).toHaveLength(0);
+
+ expect(
+ wrapper.find('EuiCallOut[data-test-subj="userCannotManageSpacesCallout"]')
+ ).toHaveLength(0);
+
+ expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1);
+ expectSaveFormButtons(wrapper);
+ });
+
+ it('renders a partial read-only view when there is a transform error', async () => {
+ const wrapper = mountWithIntl(
+
+ );
+
+ await act(async () => {
+ await nextTick();
+ wrapper.update();
+ });
+
+ expect(wrapper.find(TransformErrorSection)).toHaveLength(1);
+ expectReadOnlyFormButtons(wrapper);
+ });
+ });
+
+ it('can render if features are not available', async () => {
+ const { http } = coreMock.createStart();
+ http.get.mockImplementation(async path => {
+ if (path === '/api/features') {
+ const error = { response: { status: 404 } };
+ throw error;
+ }
+
+ if (path === '/api/spaces/space') {
+ return buildSpaces();
+ }
+ });
+
+ const wrapper = mountWithIntl();
+
+ await act(async () => {
+ await nextTick();
+ wrapper.update();
+ });
+
+ expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1);
+ expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0);
+ expectSaveFormButtons(wrapper);
+ });
+
+ it('can render if index patterns are not available', async () => {
+ const indexPatterns = dataPluginMock.createStartContract().indexPatterns;
+ indexPatterns.getTitles = jest.fn().mockRejectedValue({ response: { status: 403 } });
+
+ const wrapper = mountWithIntl(
+
+ );
+
+ await act(async () => {
+ await nextTick();
+ wrapper.update();
+ });
+
+ expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1);
+ expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0);
+ expectSaveFormButtons(wrapper);
+ });
+});
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx
new file mode 100644
index 0000000000000..33e69a68ca896
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx
@@ -0,0 +1,594 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import _, { get } from 'lodash';
+import {
+ EuiButton,
+ EuiButtonEmpty,
+ EuiFieldText,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiForm,
+ EuiFormRow,
+ EuiPanel,
+ EuiSpacer,
+ EuiText,
+ EuiTitle,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import React, {
+ ChangeEvent,
+ Fragment,
+ FunctionComponent,
+ HTMLProps,
+ useEffect,
+ useRef,
+ useState,
+} from 'react';
+import {
+ Capabilities,
+ FatalErrorsSetup,
+ HttpStart,
+ IHttpFetchError,
+ NotificationsStart,
+} from 'src/core/public';
+import { IndexPatternsContract } from '../../../../../../../src/plugins/data/public';
+import { Space } from '../../../../../spaces/common/model/space';
+import { Feature } from '../../../../../features/public';
+import {
+ KibanaPrivileges,
+ RawKibanaPrivileges,
+ Role,
+ BuiltinESPrivileges,
+ isReadOnlyRole as checkIfRoleReadOnly,
+ isReservedRole as checkIfRoleReserved,
+ copyRole,
+ prepareRoleClone,
+ RoleIndexPrivilege,
+} from '../../../../common/model';
+import { ROLES_PATH } from '../../management_urls';
+import { RoleValidationResult, RoleValidator } from './validate_role';
+import { DeleteRoleButton } from './delete_role_button';
+import { ElasticsearchPrivileges, KibanaPrivilegesRegion } from './privileges';
+import { ReservedRoleBadge } from './reserved_role_badge';
+import { SecurityLicense } from '../../../../common/licensing';
+import { UserAPIClient } from '../../users';
+import { DocumentationLinksService } from '../documentation_links';
+import { IndicesAPIClient } from '../indices_api_client';
+import { RolesAPIClient } from '../roles_api_client';
+import { PrivilegesAPIClient } from '../privileges_api_client';
+
+interface Props {
+ action: 'edit' | 'clone';
+ roleName?: string;
+ indexPatterns: IndexPatternsContract;
+ userAPIClient: PublicMethodsOf;
+ indicesAPIClient: PublicMethodsOf;
+ rolesAPIClient: PublicMethodsOf;
+ privilegesAPIClient: PublicMethodsOf;
+ docLinks: DocumentationLinksService;
+ http: HttpStart;
+ license: SecurityLicense;
+ spacesEnabled: boolean;
+ uiCapabilities: Capabilities;
+ notifications: NotificationsStart;
+ fatalErrors: FatalErrorsSetup;
+}
+
+function useRunAsUsers(
+ userAPIClient: PublicMethodsOf,
+ fatalErrors: FatalErrorsSetup
+) {
+ const [userNames, setUserNames] = useState(null);
+ useEffect(() => {
+ userAPIClient.getUsers().then(
+ users => setUserNames(users.map(user => user.username)),
+ err => fatalErrors.add(err)
+ );
+ }, [fatalErrors, userAPIClient]);
+
+ return userNames;
+}
+
+function useIndexPatternsTitles(
+ indexPatterns: IndexPatternsContract,
+ fatalErrors: FatalErrorsSetup,
+ notifications: NotificationsStart
+) {
+ const [indexPatternsTitles, setIndexPatternsTitles] = useState(null);
+ useEffect(() => {
+ indexPatterns
+ .getTitles()
+ .catch((err: IHttpFetchError) => {
+ // If user doesn't have access to the index patterns they still should be able to create new
+ // or edit existing role.
+ if (err.response?.status === 403) {
+ notifications.toasts.addDanger({
+ title: i18n.translate('xpack.security.management.roles.noIndexPatternsPermission', {
+ defaultMessage: 'You need permission to access the list of available index patterns.',
+ }),
+ });
+ return [];
+ }
+
+ fatalErrors.add(err);
+ throw err;
+ })
+ .then(setIndexPatternsTitles);
+ }, [fatalErrors, indexPatterns, notifications]);
+
+ return indexPatternsTitles;
+}
+
+function usePrivileges(
+ privilegesAPIClient: PublicMethodsOf,
+ fatalErrors: FatalErrorsSetup
+) {
+ const [privileges, setPrivileges] = useState<[RawKibanaPrivileges, BuiltinESPrivileges] | null>(
+ null
+ );
+ useEffect(() => {
+ Promise.all([
+ privilegesAPIClient.getAll({ includeActions: true }),
+ privilegesAPIClient.getBuiltIn(),
+ ]).then(
+ ([kibanaPrivileges, builtInESPrivileges]) =>
+ setPrivileges([kibanaPrivileges, builtInESPrivileges]),
+ err => fatalErrors.add(err)
+ );
+ }, [privilegesAPIClient, fatalErrors]);
+
+ return privileges;
+}
+
+function useRole(
+ rolesAPIClient: PublicMethodsOf,
+ fatalErrors: FatalErrorsSetup,
+ notifications: NotificationsStart,
+ license: SecurityLicense,
+ action: string,
+ roleName?: string
+) {
+ const [role, setRole] = useState(null);
+ useEffect(() => {
+ const rolePromise = roleName
+ ? rolesAPIClient.getRole(roleName)
+ : Promise.resolve({
+ name: '',
+ elasticsearch: { cluster: [], indices: [], run_as: [] },
+ kibana: [],
+ _unrecognized_applications: [],
+ } as Role);
+
+ rolePromise
+ .then(fetchedRole => {
+ if (action === 'clone' && checkIfRoleReserved(fetchedRole)) {
+ backToRoleList();
+ return;
+ }
+
+ if (fetchedRole.elasticsearch.indices.length === 0) {
+ const emptyOption: RoleIndexPrivilege = {
+ names: [],
+ privileges: [],
+ };
+
+ const {
+ allowRoleDocumentLevelSecurity,
+ allowRoleFieldLevelSecurity,
+ } = license.getFeatures();
+
+ if (allowRoleFieldLevelSecurity) {
+ emptyOption.field_security = {
+ grant: ['*'],
+ except: [],
+ };
+ }
+
+ if (allowRoleDocumentLevelSecurity) {
+ emptyOption.query = '';
+ }
+
+ fetchedRole.elasticsearch.indices.push(emptyOption);
+ }
+
+ setRole(action === 'clone' ? prepareRoleClone(fetchedRole) : copyRole(fetchedRole));
+ })
+ .catch((err: IHttpFetchError) => {
+ if (err.response?.status === 404) {
+ notifications.toasts.addDanger({
+ title: i18n.translate('xpack.security.management.roles.roleNotFound', {
+ defaultMessage: 'No "{roleName}" role found.',
+ values: { roleName },
+ }),
+ });
+ backToRoleList();
+ } else {
+ fatalErrors.add(err);
+ }
+ });
+ }, [roleName, action, fatalErrors, rolesAPIClient, notifications, license]);
+
+ return [role, setRole] as [Role | null, typeof setRole];
+}
+
+function useSpaces(http: HttpStart, fatalErrors: FatalErrorsSetup, spacesEnabled: boolean) {
+ const [spaces, setSpaces] = useState(null);
+ useEffect(() => {
+ (spacesEnabled ? http.get('/api/spaces/space') : Promise.resolve([])).then(
+ fetchedSpaces => setSpaces(fetchedSpaces),
+ err => fatalErrors.add(err)
+ );
+ }, [http, fatalErrors, spacesEnabled]);
+
+ return spaces;
+}
+
+function useFeatures(http: HttpStart, fatalErrors: FatalErrorsSetup) {
+ const [features, setFeatures] = useState(null);
+ useEffect(() => {
+ http
+ .get('/api/features')
+ .catch((err: IHttpFetchError) => {
+ // Currently, the `/api/features` endpoint effectively requires the "Global All" kibana privilege (e.g., what
+ // the `kibana_user` grants), because it returns information about all registered features (#35841). It's
+ // possible that a user with `manage_security` will attempt to visit the role management page without the
+ // correct Kibana privileges. If that's the case, then they receive a partial view of the role, and the UI does
+ // not allow them to make changes to that role's kibana privileges. When this user visits the edit role page,
+ // this API endpoint will throw a 404, which causes view to fail completely. So we instead attempt to detect the
+ // 404 here, and respond in a way that still allows the UI to render itself.
+ const unauthorizedForFeatures = err.response?.status === 404;
+ if (unauthorizedForFeatures) {
+ return [];
+ }
+
+ fatalErrors.add(err);
+ throw err;
+ })
+ .then(setFeatures);
+ }, [http, fatalErrors]);
+
+ return features;
+}
+
+function backToRoleList() {
+ window.location.hash = ROLES_PATH;
+}
+
+export const EditRolePage: FunctionComponent = ({
+ userAPIClient,
+ indexPatterns,
+ rolesAPIClient,
+ indicesAPIClient,
+ privilegesAPIClient,
+ http,
+ roleName,
+ action,
+ fatalErrors,
+ spacesEnabled,
+ license,
+ docLinks,
+ uiCapabilities,
+ notifications,
+}) => {
+ // We should keep the same mutable instance of Validator for every re-render since we'll
+ // eventually enable validation after the first time user tries to save a role.
+ const { current: validator } = useRef(new RoleValidator({ shouldValidate: false }));
+
+ const [formError, setFormError] = useState(null);
+ const runAsUsers = useRunAsUsers(userAPIClient, fatalErrors);
+ const indexPatternsTitles = useIndexPatternsTitles(indexPatterns, fatalErrors, notifications);
+ const privileges = usePrivileges(privilegesAPIClient, fatalErrors);
+ const spaces = useSpaces(http, fatalErrors, spacesEnabled);
+ const features = useFeatures(http, fatalErrors);
+ const [role, setRole] = useRole(
+ rolesAPIClient,
+ fatalErrors,
+ notifications,
+ license,
+ action,
+ roleName
+ );
+
+ if (!role || !runAsUsers || !indexPatternsTitles || !privileges || !spaces || !features) {
+ return null;
+ }
+
+ const isEditingExistingRole = !!roleName && action === 'edit';
+ const isReadOnlyRole = checkIfRoleReadOnly(role);
+ const isReservedRole = checkIfRoleReserved(role);
+
+ const [kibanaPrivileges, builtInESPrivileges] = privileges;
+
+ const getFormTitle = () => {
+ let titleText;
+ const props: HTMLProps = {
+ tabIndex: 0,
+ };
+ if (isReservedRole) {
+ titleText = (
+
+ );
+ props['aria-describedby'] = 'reservedRoleDescription';
+ } else if (isEditingExistingRole) {
+ titleText = (
+
+ );
+ } else {
+ titleText = (
+
+ );
+ }
+
+ return (
+
+
+ {titleText}
+
+
+ );
+ };
+
+ const getActionButton = () => {
+ if (isEditingExistingRole && !isReadOnlyRole) {
+ return (
+
+
+
+ );
+ }
+
+ return null;
+ };
+
+ const getRoleName = () => {
+ return (
+
+
+ }
+ helpText={
+ !isReservedRole && isEditingExistingRole ? (
+
+ ) : (
+ undefined
+ )
+ }
+ {...validator.validateRoleName(role)}
+ >
+
+
+
+ );
+ };
+
+ const onNameChange = (e: ChangeEvent) =>
+ setRole({
+ ...role,
+ name: e.target.value.replace(/\s/g, '_'),
+ });
+
+ const getElasticsearchPrivileges = () => {
+ return (
+
+
+
+
+ );
+ };
+
+ const onRoleChange = (newRole: Role) => setRole(newRole);
+
+ const getKibanaPrivileges = () => {
+ return (
+
+
+
+
+ );
+ };
+
+ const getFormButtons = () => {
+ if (isReadOnlyRole) {
+ return getReturnToRoleListButton();
+ }
+
+ return (
+
+ {getSaveButton()}
+ {getCancelButton()}
+
+ {getActionButton()}
+
+ );
+ };
+
+ const getReturnToRoleListButton = () => {
+ return (
+
+
+
+ );
+ };
+
+ const getSaveButton = () => {
+ const saveText = isEditingExistingRole ? (
+
+ ) : (
+
+ );
+
+ return (
+
+ {saveText}
+
+ );
+ };
+
+ const getCancelButton = () => {
+ return (
+
+
+
+ );
+ };
+
+ const saveRole = async () => {
+ validator.enableValidation();
+
+ const result = validator.validateForSave(role);
+ if (result.isInvalid) {
+ setFormError(result);
+ } else {
+ setFormError(null);
+
+ try {
+ await rolesAPIClient.saveRole({ role, spacesEnabled });
+ } catch (error) {
+ notifications.toasts.addDanger(get(error, 'data.message'));
+ return;
+ }
+
+ notifications.toasts.addSuccess(
+ i18n.translate(
+ 'xpack.security.management.editRole.roleSuccessfullySavedNotificationMessage',
+ { defaultMessage: 'Saved role' }
+ )
+ );
+
+ backToRoleList();
+ }
+ };
+
+ const handleDeleteRole = async () => {
+ try {
+ await rolesAPIClient.deleteRole(role.name);
+ } catch (error) {
+ notifications.toasts.addDanger(get(error, 'data.message'));
+ return;
+ }
+
+ notifications.toasts.addSuccess(
+ i18n.translate(
+ 'xpack.security.management.editRole.roleSuccessfullyDeletedNotificationMessage',
+ { defaultMessage: 'Deleted role' }
+ )
+ );
+
+ backToRoleList();
+ };
+
+ const description = spacesEnabled ? (
+
+ ) : (
+
+ );
+
+ return (
+
+
+ {getFormTitle()}
+
+
+
+ {description}
+
+ {isReservedRole && (
+
+
+
+
+
+
+
+
+ )}
+
+
+
+ {getRoleName()}
+
+ {getElasticsearchPrivileges()}
+
+ {getKibanaPrivileges()}
+
+
+
+ {getFormButtons()}
+
+
+ );
+};
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/index.ts b/x-pack/plugins/security/public/management/roles/edit_role/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/index.ts
rename to x-pack/plugins/security/public/management/roles/edit_role/index.ts
diff --git a/x-pack/legacy/plugins/security/public/lib/privilege_utils.test.ts b/x-pack/plugins/security/public/management/roles/edit_role/privilege_utils.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/lib/privilege_utils.test.ts
rename to x-pack/plugins/security/public/management/roles/edit_role/privilege_utils.test.ts
diff --git a/x-pack/legacy/plugins/security/public/lib/privilege_utils.ts b/x-pack/plugins/security/public/management/roles/edit_role/privilege_utils.ts
similarity index 93%
rename from x-pack/legacy/plugins/security/public/lib/privilege_utils.ts
rename to x-pack/plugins/security/public/management/roles/edit_role/privilege_utils.ts
index 74bde71dc421a..3fd8536951967 100644
--- a/x-pack/legacy/plugins/security/public/lib/privilege_utils.ts
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privilege_utils.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { RoleKibanaPrivilege } from '../../common/model';
+import { RoleKibanaPrivilege } from '../../../../common/model';
/**
* Determines if the passed privilege spec defines global privileges.
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/_index.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/_index.scss
new file mode 100644
index 0000000000000..a1a9d038065e6
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/_index.scss
@@ -0,0 +1,2 @@
+@import './privilege_feature_icon';
+@import './kibana/index';
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/_privilege_feature_icon.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/_privilege_feature_icon.scss
new file mode 100644
index 0000000000000..a7f24c96a2821
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/_privilege_feature_icon.scss
@@ -0,0 +1,4 @@
+.secPrivilegeFeatureIcon {
+ flex-shrink: 0;
+ margin-right: $euiSizeS;
+}
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/__snapshots__/cluster_privileges.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/cluster_privileges.test.tsx.snap
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/__snapshots__/cluster_privileges.test.tsx.snap
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/cluster_privileges.test.tsx.snap
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap
similarity index 87%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap
index 795131337c31f..323629de7578d 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap
@@ -15,7 +15,7 @@ exports[`it renders without crashing 1`] = `
/>
{
+ expect(shallowWithIntl()).toMatchSnapshot();
+});
+
+test('it renders ClusterPrivileges', () => {
+ expect(
+ mountWithIntl().find(ClusterPrivileges)
+ ).toHaveLength(1);
+});
+
+test('it renders IndexPrivileges', () => {
+ expect(
+ mountWithIntl().find(IndexPrivileges)
+ ).toHaveLength(1);
+});
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/elasticsearch_privileges.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx
similarity index 89%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/elasticsearch_privileges.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx
index c0e6db3fef21c..96249ccf3ff87 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/elasticsearch_privileges.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx
@@ -7,7 +7,6 @@
import {
EuiButton,
EuiComboBox,
- // @ts-ignore
EuiDescribedFormGroup,
EuiFormRow,
EuiHorizontalRule,
@@ -19,26 +18,26 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { Component, Fragment } from 'react';
-import { Role, BuiltinESPrivileges } from '../../../../../../../common/model';
-// @ts-ignore
-import { documentationLinks } from '../../../../../../documentation_links';
-import { RoleValidator } from '../../../lib/validate_role';
+import { Role, BuiltinESPrivileges } from '../../../../../../common/model';
+import { SecurityLicense } from '../../../../../../common/licensing';
+import { IndicesAPIClient } from '../../../indices_api_client';
+import { RoleValidator } from '../../validate_role';
import { CollapsiblePanel } from '../../collapsible_panel';
import { ClusterPrivileges } from './cluster_privileges';
-
import { IndexPrivileges } from './index_privileges';
+import { DocumentationLinksService } from '../../../documentation_links';
interface Props {
role: Role;
editable: boolean;
- httpClient: any;
+ indicesAPIClient: PublicMethodsOf;
+ docLinks: DocumentationLinksService;
+ license: SecurityLicense;
onChange: (role: Role) => void;
runAsUsers: string[];
validator: RoleValidator;
builtinESPrivileges: BuiltinESPrivileges;
indexPatterns: string[];
- allowDocumentLevelSecurity: boolean;
- allowFieldLevelSecurity: boolean;
}
export class ElasticsearchPrivileges extends Component {
@@ -53,23 +52,22 @@ export class ElasticsearchPrivileges extends Component {
public getForm = () => {
const {
role,
- httpClient,
+ indicesAPIClient,
+ docLinks,
validator,
onChange,
editable,
indexPatterns,
- allowDocumentLevelSecurity,
- allowFieldLevelSecurity,
+ license,
builtinESPrivileges,
} = this.props;
const indexProps = {
role,
- httpClient,
+ indicesAPIClient,
validator,
indexPatterns,
- allowDocumentLevelSecurity,
- allowFieldLevelSecurity,
+ license,
onChange,
availableIndexPrivileges: builtinESPrivileges.index,
};
@@ -91,7 +89,7 @@ export class ElasticsearchPrivileges extends Component {
id="xpack.security.management.editRole.elasticSearchPrivileges.manageRoleActionsDescription"
defaultMessage="Manage the actions this role can perform against your cluster. "
/>
- {this.learnMore(documentationLinks.esClusterPrivileges)}
+ {this.learnMore(docLinks.getESClusterPrivilegesDocUrl())}
}
>
@@ -121,7 +119,7 @@ export class ElasticsearchPrivileges extends Component {
id="xpack.security.management.editRole.elasticSearchPrivileges.howToBeSubmittedOnBehalfOfOtherUsersDescription"
defaultMessage="Allow requests to be submitted on the behalf of other users. "
/>
- {this.learnMore(documentationLinks.esRunAsPrivileges)}
+ {this.learnMore(docLinks.getESRunAsPrivilegesDocUrl())}
}
>
@@ -165,7 +163,7 @@ export class ElasticsearchPrivileges extends Component {
id="xpack.security.management.editRole.elasticSearchPrivileges.controlAccessToClusterDataDescription"
defaultMessage="Control access to the data in your cluster. "
/>
- {this.learnMore(documentationLinks.esIndicesPrivileges)}
+ {this.learnMore(docLinks.getESIndicesPrivilegesDocUrl())}
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privilege_form.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.test.tsx
similarity index 99%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privilege_form.test.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.test.tsx
index 6d386fd78a11b..5e2da51314365 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privilege_form.test.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.test.tsx
@@ -6,7 +6,7 @@
import { EuiButtonIcon, EuiTextArea } from '@elastic/eui';
import React from 'react';
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
-import { RoleValidator } from '../../../lib/validate_role';
+import { RoleValidator } from '../../validate_role';
import { IndexPrivilegeForm } from './index_privilege_form';
test('it renders without crashing', () => {
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privilege_form.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.tsx
similarity index 98%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privilege_form.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.tsx
index bafc56dc167ea..15e0367c2b6dc 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privilege_form.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.tsx
@@ -19,8 +19,8 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import _ from 'lodash';
import React, { ChangeEvent, Component, Fragment } from 'react';
-import { RoleIndexPrivilege } from '../../../../../../../common/model';
-import { RoleValidator } from '../../../lib/validate_role';
+import { RoleIndexPrivilege } from '../../../../../../common/model';
+import { RoleValidator } from '../../validate_role';
const fromOption = (option: any) => option.label;
const toOption = (value: string) => ({ label: value });
@@ -164,7 +164,6 @@ export class IndexPrivilegeForm extends Component {
{!isReadOnlyRole && (
{
- // @ts-ignore missing "compressed" prop definition
{
}
return (
- // @ts-ignore
{!this.props.isReadOnlyRole && (
{
- // @ts-ignore missing "compressed" proptype
new Promise(setImmediate);
test('it renders without crashing', async () => {
+ const license = licenseMock.create();
+ license.getFeatures.mockReturnValue({
+ allowRoleFieldLevelSecurity: true,
+ allowRoleDocumentLevelSecurity: true,
+ } as any);
+
const props = {
role: {
name: '',
@@ -25,14 +34,13 @@ test('it renders without crashing', async () => {
run_as: [],
},
},
- httpClient: jest.fn(),
onChange: jest.fn(),
indexPatterns: [],
editable: true,
- allowDocumentLevelSecurity: true,
- allowFieldLevelSecurity: true,
validator: new RoleValidator(),
availableIndexPrivileges: ['all', 'read', 'write', 'index'],
+ indicesAPIClient: indicesAPIClientMock.create(),
+ license,
};
const wrapper = shallowWithIntl();
await flushPromises();
@@ -40,6 +48,12 @@ test('it renders without crashing', async () => {
});
test('it renders a IndexPrivilegeForm for each privilege on the role', async () => {
+ const license = licenseMock.create();
+ license.getFeatures.mockReturnValue({
+ allowRoleFieldLevelSecurity: true,
+ allowRoleDocumentLevelSecurity: true,
+ } as any);
+
const props = {
role: {
name: '',
@@ -59,14 +73,13 @@ test('it renders a IndexPrivilegeForm for each privilege on the role', async ()
run_as: [],
},
},
- httpClient: jest.fn(),
onChange: jest.fn(),
indexPatterns: [],
editable: true,
- allowDocumentLevelSecurity: true,
- allowFieldLevelSecurity: true,
validator: new RoleValidator(),
availableIndexPrivileges: ['all', 'read', 'write', 'index'],
+ indicesAPIClient: indicesAPIClientMock.create(),
+ license,
};
const wrapper = mountWithIntl();
await flushPromises();
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privileges.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.tsx
similarity index 84%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privileges.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.tsx
index f09084ad2bb38..2c745067fede2 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privileges.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.tsx
@@ -5,19 +5,23 @@
*/
import _ from 'lodash';
import React, { Component, Fragment } from 'react';
-import { Role, RoleIndexPrivilege } from '../../../../../../../common/model';
-import { isReadOnlyRole, isRoleEnabled } from '../../../../../../lib/role_utils';
-import { getFields } from '../../../../../../objects';
-import { RoleValidator } from '../../../lib/validate_role';
+import {
+ Role,
+ RoleIndexPrivilege,
+ isReadOnlyRole,
+ isRoleEnabled,
+} from '../../../../../../common/model';
+import { SecurityLicense } from '../../../../../../common/licensing';
+import { IndicesAPIClient } from '../../../indices_api_client';
+import { RoleValidator } from '../../validate_role';
import { IndexPrivilegeForm } from './index_privilege_form';
interface Props {
role: Role;
indexPatterns: string[];
availableIndexPrivileges: string[];
- allowDocumentLevelSecurity: boolean;
- allowFieldLevelSecurity: boolean;
- httpClient: any;
+ indicesAPIClient: PublicMethodsOf;
+ license: SecurityLicense;
onChange: (role: Role) => void;
validator: RoleValidator;
}
@@ -43,20 +47,16 @@ export class IndexPrivileges extends Component {
public render() {
const { indices = [] } = this.props.role.elasticsearch;
- const {
- indexPatterns,
- allowDocumentLevelSecurity,
- allowFieldLevelSecurity,
- availableIndexPrivileges,
- } = this.props;
+ const { indexPatterns, license, availableIndexPrivileges } = this.props;
+ const { allowRoleDocumentLevelSecurity, allowRoleFieldLevelSecurity } = license.getFeatures();
const props = {
indexPatterns,
// If editing an existing role while that has been disabled, always show the FLS/DLS fields because currently
// a role is only marked as disabled if it has FLS/DLS setup (usually before the user changed to a license that
// doesn't permit FLS/DLS).
- allowDocumentLevelSecurity: allowDocumentLevelSecurity || !isRoleEnabled(this.props.role),
- allowFieldLevelSecurity: allowFieldLevelSecurity || !isRoleEnabled(this.props.role),
+ allowDocumentLevelSecurity: allowRoleDocumentLevelSecurity || !isRoleEnabled(this.props.role),
+ allowFieldLevelSecurity: allowRoleFieldLevelSecurity || !isRoleEnabled(this.props.role),
isReadOnlyRole: isReadOnlyRole(this.props.role),
};
@@ -171,7 +171,7 @@ export class IndexPrivileges extends Component {
try {
return {
- [pattern]: await getFields(this.props.httpClient, pattern),
+ [pattern]: await this.props.indicesAPIClient.getFields(pattern),
};
} catch (e) {
return {
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/index.ts b/x-pack/plugins/security/public/management/roles/edit_role/privileges/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/index.ts
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/__snapshots__/kibana_privileges_region.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/__snapshots__/kibana_privileges_region.test.tsx.snap
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/__snapshots__/kibana_privileges_region.test.tsx.snap
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/__snapshots__/kibana_privileges_region.test.tsx.snap
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/_index.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/_index.scss
new file mode 100644
index 0000000000000..19547c0e1953e
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/_index.scss
@@ -0,0 +1,2 @@
+@import './feature_table/index';
+@import './space_aware_privilege_section/index';
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/lib/constants.ts b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/constants.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/lib/constants.ts
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/constants.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/feature_table/__snapshots__/feature_table.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/__snapshots__/feature_table.test.tsx.snap
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/feature_table/__snapshots__/feature_table.test.tsx.snap
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/__snapshots__/feature_table.test.tsx.snap
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/feature_table/_index.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/_change_all_privileges.scss
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/feature_table/_index.scss
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/_change_all_privileges.scss
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/_index.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/_index.scss
new file mode 100644
index 0000000000000..6a96553742819
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/_index.scss
@@ -0,0 +1 @@
+@import './change_all_privileges';
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/feature_table/change_all_privileges.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/change_all_privileges.tsx
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/feature_table/change_all_privileges.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/change_all_privileges.tsx
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/feature_table/feature_table.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx
similarity index 94%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/feature_table/feature_table.test.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx
index 9648bf1d111bf..dea42e16f99d4 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/feature_table/feature_table.test.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx
@@ -3,12 +3,11 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-// @ts-ignore
import { EuiInMemoryTable } from '@elastic/eui';
import React from 'react';
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
-import { FeaturesPrivileges, KibanaPrivileges, Role } from '../../../../../../../../common/model';
-import { KibanaPrivilegeCalculatorFactory } from '../../../../../../../lib/kibana_privilege_calculator';
+import { FeaturesPrivileges, KibanaPrivileges, Role } from '../../../../../../../common/model';
+import { KibanaPrivilegeCalculatorFactory } from '../kibana_privilege_calculator';
import { FeatureTable } from './feature_table';
const defaultPrivilegeDefinition = new KibanaPrivileges({
@@ -113,7 +112,6 @@ describe('FeatureTable', () => {
onChange={jest.fn()}
onChangeAll={jest.fn()}
spacesIndex={0}
- intl={null as any}
/>
);
@@ -141,7 +139,6 @@ describe('FeatureTable', () => {
onChange={jest.fn()}
onChangeAll={jest.fn()}
spacesIndex={-1}
- intl={null as any}
/>
);
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/feature_table/feature_table.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx
similarity index 91%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/feature_table/feature_table.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx
index a05dc687fce4a..8283efe23260a 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/feature_table/feature_table.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx
@@ -4,28 +4,27 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import _ from 'lodash';
+import React, { Component } from 'react';
import {
- // @ts-ignore
EuiButtonGroup,
EuiIcon,
EuiIconTip,
- // @ts-ignore
EuiInMemoryTable,
EuiText,
IconType,
} from '@elastic/eui';
-import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react';
-import _ from 'lodash';
-import React, { Component } from 'react';
-import { Feature } from '../../../../../../../../../../../plugins/features/public';
-import { FeaturesPrivileges, KibanaPrivileges, Role } from '../../../../../../../../common/model';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { Feature } from '../../../../../../../../features/public';
+import { FeaturesPrivileges, KibanaPrivileges, Role } from '../../../../../../../common/model';
import {
AllowedPrivilege,
CalculatedPrivilege,
PrivilegeExplanation,
-} from '../../../../../../../lib/kibana_privilege_calculator';
-import { isGlobalPrivilegeDefinition } from '../../../../../../../lib/privilege_utils';
-import { NO_PRIVILEGE_VALUE } from '../../../../lib/constants';
+} from '../kibana_privilege_calculator';
+import { isGlobalPrivilegeDefinition } from '../../../privilege_utils';
+import { NO_PRIVILEGE_VALUE } from '../constants';
import { PrivilegeDisplay } from '../space_aware_privilege_section/privilege_display';
import { ChangeAllPrivilegesControl } from './change_all_privileges';
@@ -36,7 +35,6 @@ interface Props {
allowedPrivileges: AllowedPrivilege;
rankedFeaturePrivileges: FeaturesPrivileges;
kibanaPrivileges: KibanaPrivileges;
- intl: InjectedIntl;
spacesIndex: number;
onChange: (featureId: string, privileges: string[]) => void;
onChangeAll: (privileges: string[]) => void;
@@ -100,9 +98,7 @@ export class FeatureTable extends Component {
const availablePrivileges = Object.values(rankedFeaturePrivileges)[0];
return (
- // @ts-ignore missing responsive from typedef
{
private getColumns = (availablePrivileges: string[]) => [
{
field: 'feature',
- name: this.props.intl.formatMessage({
- id: 'xpack.security.management.editRole.featureTable.enabledRoleFeaturesFeatureColumnTitle',
- defaultMessage: 'Feature',
- }),
+ name: i18n.translate(
+ 'xpack.security.management.editRole.featureTable.enabledRoleFeaturesFeatureColumnTitle',
+ { defaultMessage: 'Feature' }
+ ),
render: (feature: TableFeature) => {
let tooltipElement = null;
if (feature.privilegesTooltip) {
@@ -239,9 +235,7 @@ export class FeatureTable extends Component {
});
return (
- // @ts-ignore missing name from typedef
void;
validator: RoleValidator;
- intl: InjectedIntl;
}
export class KibanaPrivilegesRegion extends Component {
@@ -81,7 +79,6 @@ export class KibanaPrivilegesRegion extends Component {
privilegeCalculatorFactory={privilegeCalculatorFactory}
onChange={onChange}
editable={editable}
- intl={this.props.intl}
/>
);
}
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/__snapshots__/simple_privilege_section.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/__snapshots__/simple_privilege_section.test.tsx.snap
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/__snapshots__/simple_privilege_section.test.tsx.snap
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/__snapshots__/simple_privilege_section.test.tsx.snap
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/index.ts b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/index.ts
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/privilege_selector.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/privilege_selector.tsx
similarity index 95%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/privilege_selector.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/privilege_selector.tsx
index 135419cc9a10d..bda0227372c09 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/privilege_selector.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/privilege_selector.tsx
@@ -6,7 +6,7 @@
import { EuiSelect } from '@elastic/eui';
import React, { ChangeEvent, Component } from 'react';
-import { NO_PRIVILEGE_VALUE } from '../../../../lib/constants';
+import { NO_PRIVILEGE_VALUE } from '../constants';
interface Props {
['data-test-subj']: string;
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx
similarity index 95%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx
index f97fa93294ff5..db1e3cfd61621 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx
@@ -3,13 +3,13 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-// @ts-ignore
+
import { EuiButtonGroup, EuiButtonGroupProps, EuiComboBox, EuiSuperSelect } from '@elastic/eui';
import React from 'react';
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
-import { Feature } from '../../../../../../../../../../../plugins/features/public';
-import { KibanaPrivileges, Role } from '../../../../../../../../common/model';
-import { KibanaPrivilegeCalculatorFactory } from '../../../../../../../lib/kibana_privilege_calculator';
+import { Feature } from '../../../../../../../../features/public';
+import { KibanaPrivileges, Role } from '../../../../../../../common/model';
+import { KibanaPrivilegeCalculatorFactory } from '../kibana_privilege_calculator';
import { SimplePrivilegeSection } from './simple_privilege_section';
import { UnsupportedSpacePrivilegesWarning } from './unsupported_space_privileges_warning';
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/simple_privilege_section.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.tsx
similarity index 94%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/simple_privilege_section.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.tsx
index 7768dc769a32f..2221fc6bab279 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/simple_privilege_section.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.tsx
@@ -6,21 +6,24 @@
import {
EuiComboBox,
- // @ts-ignore
EuiDescribedFormGroup,
EuiFormRow,
EuiSuperSelect,
EuiText,
} from '@elastic/eui';
-import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react';
+import { FormattedMessage } from '@kbn/i18n/react';
import React, { Component, Fragment } from 'react';
-import { Feature } from '../../../../../../../../../../../plugins/features/public';
-import { KibanaPrivileges, Role, RoleKibanaPrivilege } from '../../../../../../../../common/model';
-import { KibanaPrivilegeCalculatorFactory } from '../../../../../../../lib/kibana_privilege_calculator';
-import { isGlobalPrivilegeDefinition } from '../../../../../../../lib/privilege_utils';
-import { copyRole } from '../../../../../../../lib/role_utils';
-import { CUSTOM_PRIVILEGE_VALUE, NO_PRIVILEGE_VALUE } from '../../../../lib/constants';
+import { Feature } from '../../../../../../../../features/public';
+import {
+ KibanaPrivileges,
+ Role,
+ RoleKibanaPrivilege,
+ copyRole,
+} from '../../../../../../../common/model';
+import { KibanaPrivilegeCalculatorFactory } from '../kibana_privilege_calculator';
+import { isGlobalPrivilegeDefinition } from '../../../privilege_utils';
+import { CUSTOM_PRIVILEGE_VALUE, NO_PRIVILEGE_VALUE } from '../constants';
import { FeatureTable } from '../feature_table';
import { UnsupportedSpacePrivilegesWarning } from './unsupported_space_privileges_warning';
@@ -31,7 +34,6 @@ interface Props {
features: Feature[];
onChange: (role: Role) => void;
editable: boolean;
- intl: InjectedIntl;
}
interface State {
@@ -230,7 +232,6 @@ export class SimplePrivilegeSection extends Component {
allowedPrivileges={allowedPrivileges}
rankedFeaturePrivileges={privilegeCalculator.rankedFeaturePrivileges}
features={this.props.features}
- intl={this.props.intl}
onChange={this.onFeaturePrivilegeChange}
onChangeAll={this.onChangeAllFeaturePrivileges}
spacesIndex={this.props.role.kibana.findIndex(k => isGlobalPrivilegeDefinition(k))}
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/unsupported_space_privileges_warning.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/unsupported_space_privileges_warning.tsx
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/simple_privilege_section/unsupported_space_privileges_warning.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/unsupported_space_privileges_warning.tsx
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/__fixtures__/index.ts b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/__fixtures__/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/__fixtures__/index.ts
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/__fixtures__/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/__fixtures__/raw_kibana_privileges.ts b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/__fixtures__/raw_kibana_privileges.ts
similarity index 94%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/__fixtures__/raw_kibana_privileges.ts
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/__fixtures__/raw_kibana_privileges.ts
index d412ba63403e1..428836c9f181b 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/__fixtures__/raw_kibana_privileges.ts
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/__fixtures__/raw_kibana_privileges.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { RawKibanaPrivileges } from '../../../../../../../../../common/model';
+import { RawKibanaPrivileges } from '../../../../../../../../common/model';
export const rawKibanaPrivileges: RawKibanaPrivileges = {
global: {
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/__snapshots__/privilege_display.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/__snapshots__/privilege_display.test.tsx.snap
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/__snapshots__/privilege_display.test.tsx.snap
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/__snapshots__/privilege_display.test.tsx.snap
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/__snapshots__/privilege_space_form.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/__snapshots__/privilege_space_form.test.tsx.snap
similarity index 82%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/__snapshots__/privilege_space_form.test.tsx.snap
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/__snapshots__/privilege_space_form.test.tsx.snap
index c20a391cdb20c..e9f2f946e9885 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/__snapshots__/privilege_space_form.test.tsx.snap
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/__snapshots__/privilege_space_form.test.tsx.snap
@@ -351,108 +351,6 @@ exports[` renders without crashing 1`] = `
}
disabled={true}
features={Array []}
- intl={
- Object {
- "defaultFormats": Object {},
- "defaultLocale": "en",
- "formatDate": [Function],
- "formatHTMLMessage": [Function],
- "formatMessage": [Function],
- "formatNumber": [Function],
- "formatPlural": [Function],
- "formatRelative": [Function],
- "formatTime": [Function],
- "formats": Object {
- "date": Object {
- "full": Object {
- "day": "numeric",
- "month": "long",
- "weekday": "long",
- "year": "numeric",
- },
- "long": Object {
- "day": "numeric",
- "month": "long",
- "year": "numeric",
- },
- "medium": Object {
- "day": "numeric",
- "month": "short",
- "year": "numeric",
- },
- "short": Object {
- "day": "numeric",
- "month": "numeric",
- "year": "2-digit",
- },
- },
- "number": Object {
- "currency": Object {
- "style": "currency",
- },
- "percent": Object {
- "style": "percent",
- },
- },
- "relative": Object {
- "days": Object {
- "units": "day",
- },
- "hours": Object {
- "units": "hour",
- },
- "minutes": Object {
- "units": "minute",
- },
- "months": Object {
- "units": "month",
- },
- "seconds": Object {
- "units": "second",
- },
- "years": Object {
- "units": "year",
- },
- },
- "time": Object {
- "full": Object {
- "hour": "numeric",
- "minute": "numeric",
- "second": "numeric",
- "timeZoneName": "short",
- },
- "long": Object {
- "hour": "numeric",
- "minute": "numeric",
- "second": "numeric",
- "timeZoneName": "short",
- },
- "medium": Object {
- "hour": "numeric",
- "minute": "numeric",
- "second": "numeric",
- },
- "short": Object {
- "hour": "numeric",
- "minute": "numeric",
- },
- },
- },
- "formatters": Object {
- "getDateTimeFormat": [Function],
- "getMessageFormat": [Function],
- "getNumberFormat": [Function],
- "getPluralFormat": [Function],
- "getRelativeFormat": [Function],
- },
- "locale": "en",
- "messages": Object {},
- "now": [Function],
- "onError": [Function],
- "textComponent": Symbol(react.fragment),
- "timeZone": null,
- }
- }
kibanaPrivileges={
KibanaPrivileges {
"rawKibanaPrivileges": Object {
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/__snapshots__/space_aware_privilege_section.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/__snapshots__/space_aware_privilege_section.test.tsx.snap
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/__snapshots__/space_aware_privilege_section.test.tsx.snap
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/__snapshots__/space_aware_privilege_section.test.tsx.snap
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/_index.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/_index.scss
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/_index.scss
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/_index.scss
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/_privilege_matrix.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/_privilege_matrix.scss
similarity index 87%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/_privilege_matrix.scss
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/_privilege_matrix.scss
index 5f9fbced5ee6a..8f47727fdf8d6 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/_privilege_matrix.scss
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/_privilege_matrix.scss
@@ -9,6 +9,6 @@
.secPrivilegeMatrix__row--isBasePrivilege,
.secPrivilegeMatrix__cell--isGlobalPrivilege,
-.secPrivilegeTable__row--isGlobalSpace, {
+.secPrivilegeTable__row--isGlobalSpace {
background-color: $euiColorLightestShade;
}
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/index.ts b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/index.ts
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_display.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_display.test.tsx
similarity index 96%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_display.test.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_display.test.tsx
index 62e22050132fd..c6268e19abfd1 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_display.test.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_display.test.tsx
@@ -7,7 +7,7 @@
import { EuiIconTip, EuiText, EuiToolTip } from '@elastic/eui';
import React from 'react';
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
-import { PRIVILEGE_SOURCE } from '../../../../../../../lib/kibana_privilege_calculator';
+import { PRIVILEGE_SOURCE } from '../kibana_privilege_calculator';
import { PrivilegeDisplay } from './privilege_display';
describe('PrivilegeDisplay', () => {
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_display.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_display.tsx
similarity index 96%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_display.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_display.tsx
index 6af7672f6fef8..55ac99da4c8c1 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_display.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_display.tsx
@@ -7,11 +7,8 @@ import { EuiIcon, EuiIconTip, EuiText, IconType, PropsOf, EuiToolTip } from '@el
import { FormattedMessage } from '@kbn/i18n/react';
import _ from 'lodash';
import React, { ReactNode, FC } from 'react';
-import {
- PRIVILEGE_SOURCE,
- PrivilegeExplanation,
-} from '../../../../../../../lib/kibana_privilege_calculator';
-import { NO_PRIVILEGE_VALUE } from '../../../../lib/constants';
+import { PRIVILEGE_SOURCE, PrivilegeExplanation } from '../kibana_privilege_calculator';
+import { NO_PRIVILEGE_VALUE } from '../constants';
interface Props extends PropsOf {
privilege: string | string[] | undefined;
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_matrix.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_matrix.test.tsx
similarity index 89%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_matrix.test.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_matrix.test.tsx
index ee121caa13a2a..16aad4826ae44 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_matrix.test.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_matrix.test.tsx
@@ -3,14 +3,14 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-// @ts-ignore
+
import { EuiButtonEmpty, EuiInMemoryTable } from '@elastic/eui';
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
-import { Space } from '../../../../../../../../../spaces/common/model/space';
-import { Feature } from '../../../../../../../../../../../plugins/features/public';
-import { KibanaPrivileges, Role } from '../../../../../../../../common/model';
-import { KibanaPrivilegeCalculatorFactory } from '../../../../../../..//lib/kibana_privilege_calculator';
+import { Space } from '../../../../../../../../spaces/common/model/space';
+import { Feature } from '../../../../../../../../features/public';
+import { KibanaPrivileges, Role } from '../../../../../../../common/model';
+import { KibanaPrivilegeCalculatorFactory } from '../kibana_privilege_calculator';
import { PrivilegeMatrix } from './privilege_matrix';
describe('PrivilegeMatrix', () => {
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_matrix.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_matrix.tsx
similarity index 93%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_matrix.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_matrix.tsx
index 962487312c83d..b3449e32c6c91 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_matrix.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_matrix.tsx
@@ -8,7 +8,6 @@ import {
EuiButtonEmpty,
EuiIcon,
EuiIconTip,
- // @ts-ignore
EuiInMemoryTable,
EuiModal,
EuiModalBody,
@@ -16,18 +15,16 @@ import {
EuiModalHeader,
EuiModalHeaderTitle,
EuiOverlayMask,
- // @ts-ignore
- EuiToolTip,
IconType,
} from '@elastic/eui';
import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react';
import React, { Component, Fragment } from 'react';
-import { Space } from '../../../../../../../../../spaces/common/model/space';
-import { SpaceAvatar } from '../../../../../../../../../spaces/public/space_avatar';
-import { Feature } from '../../../../../../../../../../../plugins/features/public';
-import { FeaturesPrivileges, Role } from '../../../../../../../../common/model';
-import { CalculatedPrivilege } from '../../../../../../../lib/kibana_privilege_calculator';
-import { isGlobalPrivilegeDefinition } from '../../../../../../../lib/privilege_utils';
+import { SpaceAvatar } from '../../../../../../../../../legacy/plugins/spaces/public/space_avatar';
+import { Space } from '../../../../../../../../spaces/common/model/space';
+import { Feature } from '../../../../../../../../features/public';
+import { FeaturesPrivileges, Role } from '../../../../../../../common/model';
+import { CalculatedPrivilege } from '../kibana_privilege_calculator';
+import { isGlobalPrivilegeDefinition } from '../../../privilege_utils';
import { SpacesPopoverList } from '../../../spaces_popover_list';
import { PrivilegeDisplay } from './privilege_display';
@@ -258,11 +255,9 @@ export class PrivilegeMatrix extends Component {
];
return (
- // @ts-ignore missing rowProps from typedef
{
return {
className: item.feature.isBase ? 'secPrivilegeMatrix__row--isBasePrivilege' : '',
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx
similarity index 95%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx
index 2b7d87f663d72..675f02a81f9e1 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx
@@ -9,8 +9,8 @@ import { merge } from 'lodash';
// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
import { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers';
-import { KibanaPrivileges } from '../../../../../../../../common/model';
-import { KibanaPrivilegeCalculatorFactory } from '../../../../../../../lib/kibana_privilege_calculator';
+import { KibanaPrivileges } from '../../../../../../../common/model';
+import { KibanaPrivilegeCalculatorFactory } from '../kibana_privilege_calculator';
import { PrivilegeSpaceForm } from './privilege_space_form';
import { rawKibanaPrivileges } from './__fixtures__';
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx
similarity index 96%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx
index 5abb87d23bb6e..6d1f5117c52e9 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx
@@ -24,17 +24,16 @@ import {
} from '@elastic/eui';
import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react';
import React, { Component, Fragment } from 'react';
-import { Space } from '../../../../../../../../../spaces/common/model/space';
-import { Feature } from '../../../../../../../../../../../plugins/features/public';
-import { KibanaPrivileges, Role } from '../../../../../../../../common/model';
+import { Space } from '../../../../../../../../spaces/common/model/space';
+import { Feature } from '../../../../../../../../features/public';
+import { KibanaPrivileges, Role, copyRole } from '../../../../../../../common/model';
import {
AllowedPrivilege,
KibanaPrivilegeCalculatorFactory,
PrivilegeExplanation,
-} from '../../../../../../../lib/kibana_privilege_calculator';
-import { hasAssignedFeaturePrivileges } from '../../../../../../../lib/privilege_utils';
-import { copyRole } from '../../../../../../../lib/role_utils';
-import { CUSTOM_PRIVILEGE_VALUE } from '../../../../lib/constants';
+} from '../kibana_privilege_calculator';
+import { hasAssignedFeaturePrivileges } from '../../../privilege_utils';
+import { CUSTOM_PRIVILEGE_VALUE } from '../constants';
import { FeatureTable } from '../feature_table';
import { SpaceSelector } from './space_selector';
@@ -285,7 +284,6 @@ export class PrivilegeSpaceForm extends Component {
calculatedPrivileges={calculatedPrivileges}
allowedPrivileges={allowedPrivileges}
rankedFeaturePrivileges={privilegeCalculator.rankedFeaturePrivileges}
- intl={this.props.intl}
onChange={this.onFeaturePrivilegesChange}
onChangeAll={this.onChangeAllFeaturePrivileges}
kibanaPrivileges={this.props.kibanaPrivileges}
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_space_table.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.test.tsx
similarity index 99%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_space_table.test.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.test.tsx
index 37ee43c5473b0..f0a391c98c910 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_space_table.test.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.test.tsx
@@ -10,8 +10,8 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { ReactWrapper } from 'enzyme';
import { PrivilegeSpaceTable } from './privilege_space_table';
import { PrivilegeDisplay } from './privilege_display';
-import { KibanaPrivileges, Role, RoleKibanaPrivilege } from '../../../../../../../../common/model';
-import { KibanaPrivilegeCalculatorFactory } from '../../../../../../../lib/kibana_privilege_calculator';
+import { KibanaPrivileges, Role, RoleKibanaPrivilege } from '../../../../../../../common/model';
+import { KibanaPrivilegeCalculatorFactory } from '../kibana_privilege_calculator';
import { rawKibanaPrivileges } from './__fixtures__';
interface TableRow {
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx
similarity index 94%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx
index 65a3df9fb47a1..1c27ec84f50dc 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx
@@ -12,22 +12,21 @@ import {
EuiBasicTableColumn,
} from '@elastic/eui';
import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react';
-import _ from 'lodash';
import React, { Component } from 'react';
-import { getSpaceColor } from '../../../../../../../../../spaces/public/space_avatar';
-import { Space } from '../../../../../../../../../spaces/common/model/space';
+import { getSpaceColor } from '../../../../../../../../../legacy/plugins/spaces/public/space_avatar';
+import { Space } from '../../../../../../../../spaces/common/model/space';
import {
FeaturesPrivileges,
Role,
RoleKibanaPrivilege,
-} from '../../../../../../../../common/model';
-import { KibanaPrivilegeCalculatorFactory } from '../../../../../../../lib/kibana_privilege_calculator';
+ copyRole,
+} from '../../../../../../../common/model';
+import { KibanaPrivilegeCalculatorFactory } from '../kibana_privilege_calculator';
import {
isGlobalPrivilegeDefinition,
hasAssignedFeaturePrivileges,
-} from '../../../../../../../lib/privilege_utils';
-import { copyRole } from '../../../../../../../lib/role_utils';
-import { CUSTOM_PRIVILEGE_VALUE, NO_PRIVILEGE_VALUE } from '../../../../lib/constants';
+} from '../../../privilege_utils';
+import { CUSTOM_PRIVILEGE_VALUE, NO_PRIVILEGE_VALUE } from '../constants';
import { SpacesPopoverList } from '../../../spaces_popover_list';
import { PrivilegeDisplay } from './privilege_display';
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.test.tsx
similarity index 95%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.test.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.test.tsx
index 2756b1c447274..e06d2a4f7dc33 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.test.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.test.tsx
@@ -6,9 +6,9 @@
import React from 'react';
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
-import { KibanaPrivileges } from '../../../../../../../../common/model';
-import { KibanaPrivilegeCalculatorFactory } from '../../../../../../../lib/kibana_privilege_calculator';
-import { RoleValidator } from '../../../../lib/validate_role';
+import { KibanaPrivileges } from '../../../../../../../common/model';
+import { KibanaPrivilegeCalculatorFactory } from '../kibana_privilege_calculator';
+import { RoleValidator } from '../../../validate_role';
import { PrivilegeMatrix } from './privilege_matrix';
import { PrivilegeSpaceForm } from './privilege_space_form';
import { PrivilegeSpaceTable } from './privilege_space_table';
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx
similarity index 93%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx
index d324cf99c8418..21cadfafe1790 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx
@@ -14,13 +14,12 @@ import {
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
import _ from 'lodash';
import React, { Component, Fragment } from 'react';
-import { UICapabilities } from 'ui/capabilities';
-import { Space } from '../../../../../../../../../spaces/common/model/space';
-import { Feature } from '../../../../../../../../../../../plugins/features/public';
-import { KibanaPrivileges, Role } from '../../../../../../../../common/model';
-import { KibanaPrivilegeCalculatorFactory } from '../../../../../../../lib/kibana_privilege_calculator';
-import { isReservedRole } from '../../../../../../../lib/role_utils';
-import { RoleValidator } from '../../../../lib/validate_role';
+import { Capabilities } from 'src/core/public';
+import { Space } from '../../../../../../../../spaces/common/model/space';
+import { Feature } from '../../../../../../../../features/public';
+import { KibanaPrivileges, Role, isReservedRole } from '../../../../../../../common/model';
+import { KibanaPrivilegeCalculatorFactory } from '../kibana_privilege_calculator';
+import { RoleValidator } from '../../../validate_role';
import { PrivilegeMatrix } from './privilege_matrix';
import { PrivilegeSpaceForm } from './privilege_space_form';
import { PrivilegeSpaceTable } from './privilege_space_table';
@@ -34,7 +33,7 @@ interface Props {
editable: boolean;
validator: RoleValidator;
intl: InjectedIntl;
- uiCapabilities: UICapabilities;
+ uiCapabilities: Capabilities;
features: Feature[];
}
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/space_selector.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_selector.tsx
similarity index 93%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/space_selector.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_selector.tsx
index 0eb9cf0b0ee9d..cfeb5b9f37d8c 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/space_aware_privilege_section/space_selector.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_selector.tsx
@@ -7,8 +7,8 @@
import { EuiComboBox, EuiComboBoxOptionProps, EuiHealth, EuiHighlight } from '@elastic/eui';
import { InjectedIntl } from '@kbn/i18n/react';
import React, { Component } from 'react';
-import { Space } from '../../../../../../../../../spaces/common/model/space';
-import { getSpaceColor } from '../../../../../../../../../spaces/public/space_avatar';
+import { getSpaceColor } from '../../../../../../../../../legacy/plugins/spaces/public/space_avatar';
+import { Space } from '../../../../../../../../spaces/common/model/space';
const spaceToOption = (space?: Space, currentSelection?: 'global' | 'spaces') => {
if (!space) {
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/transform_error_section/index.ts b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/transform_error_section/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/transform_error_section/index.ts
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/transform_error_section/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/transform_error_section/transform_error_section.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/transform_error_section/transform_error_section.tsx
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/kibana/transform_error_section/transform_error_section.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/transform_error_section/transform_error_section.tsx
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/reserved_role_badge.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/reserved_role_badge.test.tsx
similarity index 96%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/reserved_role_badge.test.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/reserved_role_badge.test.tsx
index 9b483d92cde41..d29b442420a90 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/reserved_role_badge.test.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/reserved_role_badge.test.tsx
@@ -7,7 +7,7 @@
import { EuiIcon } from '@elastic/eui';
import { shallow } from 'enzyme';
import React from 'react';
-import { Role } from '../../../../../common/model';
+import { Role } from '../../../../common/model';
import { ReservedRoleBadge } from './reserved_role_badge';
const reservedRole: Role = {
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/reserved_role_badge.tsx b/x-pack/plugins/security/public/management/roles/edit_role/reserved_role_badge.tsx
similarity index 89%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/reserved_role_badge.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/reserved_role_badge.tsx
index 3d817d1e07d21..501ca7589dafd 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/reserved_role_badge.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/reserved_role_badge.tsx
@@ -8,8 +8,7 @@ import React from 'react';
import { EuiIcon, EuiToolTip } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
-import { Role } from '../../../../../common/model';
-import { isReservedRole } from '../../../../lib/role_utils';
+import { Role, isReservedRole } from '../../../../common/model';
interface Props {
role: Role;
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/_index.scss b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/_index.scss
new file mode 100644
index 0000000000000..b40a32cb8df96
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/_index.scss
@@ -0,0 +1 @@
+@import './spaces_popover_list';
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/spaces_popover_list/_spaces_popover_list.scss b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/_spaces_popover_list.scss
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/spaces_popover_list/_spaces_popover_list.scss
rename to x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/_spaces_popover_list.scss
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/spaces_popover_list/index.ts b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/spaces_popover_list/index.ts
rename to x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/spaces_popover_list/spaces_popover_list.tsx b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx
similarity index 95%
rename from x-pack/legacy/plugins/security/public/views/management/edit_role/components/spaces_popover_list/spaces_popover_list.tsx
rename to x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx
index a99e389044eaa..bb7a6db97f7c8 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/spaces_popover_list/spaces_popover_list.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx
@@ -14,9 +14,9 @@ import {
} from '@elastic/eui';
import { FormattedMessage, InjectedIntl } from '@kbn/i18n/react';
import React, { Component } from 'react';
-import { SpaceAvatar } from '../../../../../../../spaces/public/space_avatar';
-import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../../../../../../../plugins/spaces/common/constants';
-import { Space } from '../../../../../../../../../plugins/spaces/common/model/space';
+import { SpaceAvatar } from '../../../../../../../legacy/plugins/spaces/public/space_avatar';
+import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../../../../spaces/common';
+import { Space } from '../../../../../../spaces/common/model/space';
interface Props {
spaces: Space[];
@@ -146,7 +146,6 @@ export class SpacesPopoverList extends Component {
return (
{
- // @ts-ignore onSearch isn't defined on the type
({
+ getFields: jest.fn(),
+ }),
+};
diff --git a/x-pack/plugins/security/public/management/roles/indices_api_client.ts b/x-pack/plugins/security/public/management/roles/indices_api_client.ts
new file mode 100644
index 0000000000000..65d9a40a776eb
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/indices_api_client.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { HttpStart } from 'src/core/public';
+
+export class IndicesAPIClient {
+ constructor(private readonly http: HttpStart) {}
+
+ async getFields(query: string) {
+ return (
+ (await this.http.get(`/internal/security/fields/${encodeURIComponent(query)}`)) ||
+ []
+ );
+ }
+}
diff --git a/x-pack/plugins/security/public/management/roles/privileges_api_client.mock.ts b/x-pack/plugins/security/public/management/roles/privileges_api_client.mock.ts
new file mode 100644
index 0000000000000..2564914a1d3d8
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/privileges_api_client.mock.ts
@@ -0,0 +1,12 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export const privilegesAPIClientMock = {
+ create: () => ({
+ getAll: jest.fn(),
+ getBuiltIn: jest.fn(),
+ }),
+};
diff --git a/x-pack/plugins/security/public/management/roles/privileges_api_client.ts b/x-pack/plugins/security/public/management/roles/privileges_api_client.ts
new file mode 100644
index 0000000000000..45bd2fd8fb3a6
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/privileges_api_client.ts
@@ -0,0 +1,22 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { HttpStart } from 'src/core/public';
+import { BuiltinESPrivileges, RawKibanaPrivileges } from '../../../common/model';
+
+export class PrivilegesAPIClient {
+ constructor(private readonly http: HttpStart) {}
+
+ async getAll({ includeActions }: { includeActions: boolean }) {
+ return await this.http.get('/api/security/privileges', {
+ query: { includeActions },
+ });
+ }
+
+ async getBuiltIn() {
+ return await this.http.get('/internal/security/esPrivileges/builtin');
+ }
+}
diff --git a/x-pack/plugins/security/public/management/roles/roles_api_client.mock.ts b/x-pack/plugins/security/public/management/roles/roles_api_client.mock.ts
new file mode 100644
index 0000000000000..c4d3724c0ecb5
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/roles_api_client.mock.ts
@@ -0,0 +1,14 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export const rolesAPIClientMock = {
+ create: () => ({
+ getRoles: jest.fn(),
+ getRole: jest.fn(),
+ deleteRole: jest.fn(),
+ saveRole: jest.fn(),
+ }),
+};
diff --git a/x-pack/legacy/plugins/security/public/lib/transform_role_for_save.test.ts b/x-pack/plugins/security/public/management/roles/roles_api_client.test.ts
similarity index 82%
rename from x-pack/legacy/plugins/security/public/lib/transform_role_for_save.test.ts
rename to x-pack/plugins/security/public/management/roles/roles_api_client.test.ts
index 1ea19f2637305..7561161368405 100644
--- a/x-pack/legacy/plugins/security/public/lib/transform_role_for_save.test.ts
+++ b/x-pack/plugins/security/public/management/roles/roles_api_client.test.ts
@@ -4,12 +4,23 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { Role } from '../../common/model';
-import { transformRoleForSave } from './transform_role_for_save';
+import { Role } from '../../../common/model';
+import { RolesAPIClient } from './roles_api_client';
+import { httpServiceMock } from '../../../../../../src/core/public/mocks';
+
+describe('RolesAPIClient', () => {
+ async function saveRole(role: Role, spacesEnabled: boolean) {
+ const httpMock = httpServiceMock.createStartContract();
+ const rolesAPIClient = new RolesAPIClient(httpMock);
+
+ await rolesAPIClient.saveRole({ role, spacesEnabled });
+ expect(httpMock.put).toHaveBeenCalledTimes(1);
+
+ return JSON.parse(httpMock.put.mock.calls[0][1]?.body as any);
+ }
-describe('transformRoleForSave', () => {
describe('spaces disabled', () => {
- it('removes placeholder index privileges', () => {
+ it('removes placeholder index privileges', async () => {
const role: Role = {
name: 'my role',
elasticsearch: {
@@ -20,7 +31,7 @@ describe('transformRoleForSave', () => {
kibana: [],
};
- const result = transformRoleForSave(role, false);
+ const result = await saveRole(role, false);
expect(result).toEqual({
elasticsearch: {
@@ -32,7 +43,7 @@ describe('transformRoleForSave', () => {
});
});
- it('removes placeholder query entries', () => {
+ it('removes placeholder query entries', async () => {
const role: Role = {
name: 'my role',
elasticsearch: {
@@ -43,7 +54,7 @@ describe('transformRoleForSave', () => {
kibana: [],
};
- const result = transformRoleForSave(role, false);
+ const result = await saveRole(role, false);
expect(result).toEqual({
elasticsearch: {
@@ -55,7 +66,7 @@ describe('transformRoleForSave', () => {
});
});
- it('removes transient fields not required for save', () => {
+ it('removes transient fields not required for save', async () => {
const role: Role = {
name: 'my role',
transient_metadata: {
@@ -74,7 +85,7 @@ describe('transformRoleForSave', () => {
kibana: [],
};
- const result = transformRoleForSave(role, false);
+ const result = await saveRole(role, false);
expect(result).toEqual({
metadata: {
@@ -89,7 +100,7 @@ describe('transformRoleForSave', () => {
});
});
- it('does not remove actual query entries', () => {
+ it('does not remove actual query entries', async () => {
const role: Role = {
name: 'my role',
elasticsearch: {
@@ -100,7 +111,7 @@ describe('transformRoleForSave', () => {
kibana: [],
};
- const result = transformRoleForSave(role, false);
+ const result = await saveRole(role, false);
expect(result).toEqual({
elasticsearch: {
@@ -112,7 +123,7 @@ describe('transformRoleForSave', () => {
});
});
- it('should remove feature privileges if a corresponding base privilege is defined', () => {
+ it('should remove feature privileges if a corresponding base privilege is defined', async () => {
const role: Role = {
name: 'my role',
elasticsearch: {
@@ -132,7 +143,7 @@ describe('transformRoleForSave', () => {
],
};
- const result = transformRoleForSave(role, false);
+ const result = await saveRole(role, false);
expect(result).toEqual({
elasticsearch: {
@@ -150,7 +161,7 @@ describe('transformRoleForSave', () => {
});
});
- it('should not remove feature privileges if a corresponding base privilege is not defined', () => {
+ it('should not remove feature privileges if a corresponding base privilege is not defined', async () => {
const role: Role = {
name: 'my role',
elasticsearch: {
@@ -170,7 +181,7 @@ describe('transformRoleForSave', () => {
],
};
- const result = transformRoleForSave(role, false);
+ const result = await saveRole(role, false);
expect(result).toEqual({
elasticsearch: {
@@ -191,7 +202,7 @@ describe('transformRoleForSave', () => {
});
});
- it('should remove space privileges', () => {
+ it('should remove space privileges', async () => {
const role: Role = {
name: 'my role',
elasticsearch: {
@@ -219,7 +230,7 @@ describe('transformRoleForSave', () => {
],
};
- const result = transformRoleForSave(role, false);
+ const result = await saveRole(role, false);
expect(result).toEqual({
elasticsearch: {
@@ -242,7 +253,7 @@ describe('transformRoleForSave', () => {
});
describe('spaces enabled', () => {
- it('removes placeholder index privileges', () => {
+ it('removes placeholder index privileges', async () => {
const role: Role = {
name: 'my role',
elasticsearch: {
@@ -253,7 +264,7 @@ describe('transformRoleForSave', () => {
kibana: [],
};
- const result = transformRoleForSave(role, true);
+ const result = await saveRole(role, true);
expect(result).toEqual({
elasticsearch: {
@@ -265,7 +276,7 @@ describe('transformRoleForSave', () => {
});
});
- it('removes placeholder query entries', () => {
+ it('removes placeholder query entries', async () => {
const role: Role = {
name: 'my role',
elasticsearch: {
@@ -276,7 +287,7 @@ describe('transformRoleForSave', () => {
kibana: [],
};
- const result = transformRoleForSave(role, true);
+ const result = await saveRole(role, true);
expect(result).toEqual({
elasticsearch: {
@@ -288,7 +299,7 @@ describe('transformRoleForSave', () => {
});
});
- it('removes transient fields not required for save', () => {
+ it('removes transient fields not required for save', async () => {
const role: Role = {
name: 'my role',
transient_metadata: {
@@ -307,7 +318,7 @@ describe('transformRoleForSave', () => {
kibana: [],
};
- const result = transformRoleForSave(role, true);
+ const result = await saveRole(role, true);
expect(result).toEqual({
metadata: {
@@ -322,7 +333,7 @@ describe('transformRoleForSave', () => {
});
});
- it('does not remove actual query entries', () => {
+ it('does not remove actual query entries', async () => {
const role: Role = {
name: 'my role',
elasticsearch: {
@@ -333,7 +344,7 @@ describe('transformRoleForSave', () => {
kibana: [],
};
- const result = transformRoleForSave(role, true);
+ const result = await saveRole(role, true);
expect(result).toEqual({
elasticsearch: {
@@ -345,7 +356,7 @@ describe('transformRoleForSave', () => {
});
});
- it('should remove feature privileges if a corresponding base privilege is defined', () => {
+ it('should remove feature privileges if a corresponding base privilege is defined', async () => {
const role: Role = {
name: 'my role',
elasticsearch: {
@@ -365,7 +376,7 @@ describe('transformRoleForSave', () => {
],
};
- const result = transformRoleForSave(role, true);
+ const result = await saveRole(role, true);
expect(result).toEqual({
elasticsearch: {
@@ -383,7 +394,7 @@ describe('transformRoleForSave', () => {
});
});
- it('should not remove feature privileges if a corresponding base privilege is not defined', () => {
+ it('should not remove feature privileges if a corresponding base privilege is not defined', async () => {
const role: Role = {
name: 'my role',
elasticsearch: {
@@ -403,7 +414,7 @@ describe('transformRoleForSave', () => {
],
};
- const result = transformRoleForSave(role, true);
+ const result = await saveRole(role, true);
expect(result).toEqual({
elasticsearch: {
@@ -424,7 +435,7 @@ describe('transformRoleForSave', () => {
});
});
- it('should not remove space privileges', () => {
+ it('should not remove space privileges', async () => {
const role: Role = {
name: 'my role',
elasticsearch: {
@@ -452,7 +463,7 @@ describe('transformRoleForSave', () => {
],
};
- const result = transformRoleForSave(role, true);
+ const result = await saveRole(role, true);
expect(result).toEqual({
elasticsearch: {
diff --git a/x-pack/plugins/security/public/management/roles/roles_api_client.ts b/x-pack/plugins/security/public/management/roles/roles_api_client.ts
new file mode 100644
index 0000000000000..d7e98e03a965b
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/roles_api_client.ts
@@ -0,0 +1,62 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { HttpStart } from 'src/core/public';
+import { Role, RoleIndexPrivilege, copyRole } from '../../../common/model';
+import { isGlobalPrivilegeDefinition } from './edit_role/privilege_utils';
+
+export class RolesAPIClient {
+ constructor(private readonly http: HttpStart) {}
+
+ public async getRoles() {
+ return await this.http.get('/api/security/role');
+ }
+
+ public async getRole(roleName: string) {
+ return await this.http.get(`/api/security/role/${encodeURIComponent(roleName)}`);
+ }
+
+ public async deleteRole(roleName: string) {
+ await this.http.delete(`/api/security/role/${encodeURIComponent(roleName)}`);
+ }
+
+ public async saveRole({ role, spacesEnabled }: { role: Role; spacesEnabled: boolean }) {
+ await this.http.put(`/api/security/role/${encodeURIComponent(role.name)}`, {
+ body: JSON.stringify(this.transformRoleForSave(copyRole(role), spacesEnabled)),
+ });
+ }
+
+ private transformRoleForSave(role: Role, spacesEnabled: boolean) {
+ // Remove any placeholder index privileges
+ const isPlaceholderPrivilege = (indexPrivilege: RoleIndexPrivilege) =>
+ indexPrivilege.names.length === 0;
+ role.elasticsearch.indices = role.elasticsearch.indices.filter(
+ indexPrivilege => !isPlaceholderPrivilege(indexPrivilege)
+ );
+
+ // Remove any placeholder query entries
+ role.elasticsearch.indices.forEach(index => index.query || delete index.query);
+
+ // If spaces are disabled, then do not persist any space privileges
+ if (!spacesEnabled) {
+ role.kibana = role.kibana.filter(isGlobalPrivilegeDefinition);
+ }
+
+ role.kibana.forEach(kibanaPrivilege => {
+ // If a base privilege is defined, then do not persist feature privileges
+ if (kibanaPrivilege.base.length > 0) {
+ kibanaPrivilege.feature = {};
+ }
+ });
+
+ delete role.name;
+ delete role.transient_metadata;
+ delete role._unrecognized_applications;
+ delete role._transform_error;
+
+ return role;
+ }
+}
diff --git a/x-pack/legacy/plugins/security/public/views/management/roles_grid/components/__snapshots__/roles_grid_page.test.tsx.snap b/x-pack/plugins/security/public/management/roles/roles_grid/__snapshots__/roles_grid_page.test.tsx.snap
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/roles_grid/components/__snapshots__/roles_grid_page.test.tsx.snap
rename to x-pack/plugins/security/public/management/roles/roles_grid/__snapshots__/roles_grid_page.test.tsx.snap
diff --git a/x-pack/legacy/plugins/security/public/views/management/roles_grid/components/confirm_delete/confirm_delete.tsx b/x-pack/plugins/security/public/management/roles/roles_grid/confirm_delete/confirm_delete.tsx
similarity index 72%
rename from x-pack/legacy/plugins/security/public/views/management/roles_grid/components/confirm_delete/confirm_delete.tsx
rename to x-pack/plugins/security/public/management/roles/roles_grid/confirm_delete/confirm_delete.tsx
index 34784b4b2accb..37eed3357241d 100644
--- a/x-pack/legacy/plugins/security/public/views/management/roles_grid/components/confirm_delete/confirm_delete.tsx
+++ b/x-pack/plugins/security/public/management/roles/roles_grid/confirm_delete/confirm_delete.tsx
@@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import React, { Component, Fragment } from 'react';
import {
EuiButton,
EuiButtonEmpty,
@@ -15,23 +16,24 @@ import {
EuiOverlayMask,
EuiText,
} from '@elastic/eui';
-import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
-import React, { Component, Fragment } from 'react';
-import { toastNotifications } from 'ui/notify';
-import { RolesApi } from '../../../../../lib/roles_api';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { NotificationsStart } from 'src/core/public';
+import { RolesAPIClient } from '../../roles_api_client';
interface Props {
rolesToDelete: string[];
- intl: InjectedIntl;
callback: (rolesToDelete: string[], errors: string[]) => void;
onCancel: () => void;
+ notifications: NotificationsStart;
+ rolesAPIClient: PublicMethodsOf;
}
interface State {
deleteInProgress: boolean;
}
-class ConfirmDeleteUI extends Component {
+export class ConfirmDelete extends Component {
constructor(props: Props) {
super(props);
this.state = {
@@ -40,15 +42,12 @@ class ConfirmDeleteUI extends Component {
}
public render() {
- const { rolesToDelete, intl } = this.props;
+ const { rolesToDelete } = this.props;
const moreThanOne = rolesToDelete.length > 1;
- const title = intl.formatMessage(
- {
- id: 'xpack.security.management.roles.deleteRoleTitle',
- defaultMessage: 'Delete role{value, plural, one {{roleName}} other {s}}',
- },
- { value: rolesToDelete.length, roleName: ` ${rolesToDelete[0]}` }
- );
+ const title = i18n.translate('xpack.security.management.roles.deleteRoleTitle', {
+ defaultMessage: 'Delete role{value, plural, one {{roleName}} other {s}}',
+ values: { value: rolesToDelete.length, roleName: ` ${rolesToDelete[0]}` },
+ });
// This is largely the same as the built-in EuiConfirmModal component, but we needed the ability
// to disable the buttons since this could be a long-running operation
@@ -128,32 +127,24 @@ class ConfirmDeleteUI extends Component {
};
private deleteRoles = async () => {
- const { rolesToDelete, callback } = this.props;
+ const { rolesToDelete, callback, rolesAPIClient, notifications } = this.props;
const errors: string[] = [];
const deleteOperations = rolesToDelete.map(roleName => {
const deleteRoleTask = async () => {
try {
- await RolesApi.deleteRole(roleName);
- toastNotifications.addSuccess(
- this.props.intl.formatMessage(
- {
- id:
- 'xpack.security.management.roles.confirmDelete.roleSuccessfullyDeletedNotificationMessage',
- defaultMessage: 'Deleted role {roleName}',
- },
- { roleName }
+ await rolesAPIClient.deleteRole(roleName);
+ notifications.toasts.addSuccess(
+ i18n.translate(
+ 'xpack.security.management.roles.confirmDelete.roleSuccessfullyDeletedNotificationMessage',
+ { defaultMessage: 'Deleted role {roleName}', values: { roleName } }
)
);
} catch (e) {
errors.push(roleName);
- toastNotifications.addDanger(
- this.props.intl.formatMessage(
- {
- id:
- 'xpack.security.management.roles.confirmDelete.roleDeletingErrorNotificationMessage',
- defaultMessage: 'Error deleting role {roleName}',
- },
- { roleName }
+ notifications.toasts.addDanger(
+ i18n.translate(
+ 'xpack.security.management.roles.confirmDelete.roleDeletingErrorNotificationMessage',
+ { defaultMessage: 'Error deleting role {roleName}', values: { roleName } }
)
);
}
@@ -167,5 +158,3 @@ class ConfirmDeleteUI extends Component {
callback(rolesToDelete, errors);
};
}
-
-export const ConfirmDelete = injectI18n(ConfirmDeleteUI);
diff --git a/x-pack/legacy/plugins/security/public/views/management/roles_grid/components/confirm_delete/index.ts b/x-pack/plugins/security/public/management/roles/roles_grid/confirm_delete/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/roles_grid/components/confirm_delete/index.ts
rename to x-pack/plugins/security/public/management/roles/roles_grid/confirm_delete/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/roles_grid/components/index.ts b/x-pack/plugins/security/public/management/roles/roles_grid/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/roles_grid/components/index.ts
rename to x-pack/plugins/security/public/management/roles/roles_grid/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/roles_grid/components/permission_denied/index.ts b/x-pack/plugins/security/public/management/roles/roles_grid/permission_denied/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/roles_grid/components/permission_denied/index.ts
rename to x-pack/plugins/security/public/management/roles/roles_grid/permission_denied/index.ts
diff --git a/x-pack/legacy/plugins/security/public/views/management/roles_grid/components/permission_denied/permission_denied.tsx b/x-pack/plugins/security/public/management/roles/roles_grid/permission_denied/permission_denied.tsx
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/roles_grid/components/permission_denied/permission_denied.tsx
rename to x-pack/plugins/security/public/management/roles/roles_grid/permission_denied/permission_denied.tsx
diff --git a/x-pack/legacy/plugins/security/public/views/management/roles_grid/components/roles_grid_page.test.tsx b/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.test.tsx
similarity index 63%
rename from x-pack/legacy/plugins/security/public/views/management/roles_grid/components/roles_grid_page.test.tsx
rename to x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.test.tsx
index 6da2f2442d488..63ace53420612 100644
--- a/x-pack/legacy/plugins/security/public/views/management/roles_grid/components/roles_grid_page.test.tsx
+++ b/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.test.tsx
@@ -4,50 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/
-let mockSimulate403 = false;
-const mock403 = () => ({ body: { statusCode: 403 } });
-jest.mock('../../../../lib/roles_api', () => {
- return {
- RolesApi: {
- async getRoles() {
- if (mockSimulate403) {
- throw mock403();
- }
- return [
- {
- name: 'test-role-1',
- elasticsearch: { cluster: [], indices: [], run_as: [] },
- kibana: { global: [], space: {} },
- },
- {
- name: 'reserved-role',
- elasticsearch: { cluster: [], indices: [], run_as: [] },
- kibana: { global: [], space: {} },
- metadata: {
- _reserved: true,
- },
- },
- {
- name: 'disabled-role',
- elasticsearch: { cluster: [], indices: [], run_as: [] },
- kibana: { global: [], space: {} },
- transient_metadata: {
- enabled: false,
- },
- },
- ];
- },
- },
- };
-});
-
import { EuiIcon } from '@elastic/eui';
import { ReactWrapper } from 'enzyme';
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
+import { RolesAPIClient } from '../roles_api_client';
import { PermissionDenied } from './permission_denied';
import { RolesGridPage } from './roles_grid_page';
+import { coreMock } from '../../../../../../../src/core/public/mocks';
+import { rolesAPIClientMock } from '../index.mock';
+
+const mock403 = () => ({ body: { statusCode: 403 } });
+
const waitForRender = async (
wrapper: ReactWrapper,
condition: (wrapper: ReactWrapper) => boolean
@@ -69,12 +38,37 @@ const waitForRender = async (
};
describe('', () => {
+ let apiClientMock: jest.Mocked>;
beforeEach(() => {
- mockSimulate403 = false;
+ apiClientMock = rolesAPIClientMock.create();
+ apiClientMock.getRoles.mockResolvedValue([
+ {
+ name: 'test-role-1',
+ elasticsearch: { cluster: [], indices: [], run_as: [] },
+ kibana: [{ base: [], spaces: [], feature: {} }],
+ },
+ {
+ name: 'reserved-role',
+ elasticsearch: { cluster: [], indices: [], run_as: [] },
+ kibana: [{ base: [], spaces: [], feature: {} }],
+ metadata: { _reserved: true },
+ },
+ {
+ name: 'disabled-role',
+ elasticsearch: { cluster: [], indices: [], run_as: [] },
+ kibana: [{ base: [], spaces: [], feature: {} }],
+ transient_metadata: { enabled: false },
+ },
+ ]);
});
it(`renders reserved roles as such`, async () => {
- const wrapper = mountWithIntl();
+ const wrapper = mountWithIntl(
+
+ );
const initialIconCount = wrapper.find(EuiIcon).length;
await waitForRender(wrapper, updatedWrapper => {
@@ -87,8 +81,14 @@ describe('', () => {
});
it('renders permission denied if required', async () => {
- mockSimulate403 = true;
- const wrapper = mountWithIntl();
+ apiClientMock.getRoles.mockRejectedValue(mock403());
+
+ const wrapper = mountWithIntl(
+
+ );
await waitForRender(wrapper, updatedWrapper => {
return updatedWrapper.find(PermissionDenied).length > 0;
});
@@ -96,7 +96,12 @@ describe('', () => {
});
it('renders role actions as appropriate', async () => {
- const wrapper = mountWithIntl();
+ const wrapper = mountWithIntl(
+
+ );
const initialIconCount = wrapper.find(EuiIcon).length;
await waitForRender(wrapper, updatedWrapper => {
diff --git a/x-pack/legacy/plugins/security/public/views/management/roles_grid/components/roles_grid_page.tsx b/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx
similarity index 78%
rename from x-pack/legacy/plugins/security/public/views/management/roles_grid/components/roles_grid_page.tsx
rename to x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx
index 2083a93f4b33c..7c686bef391a7 100644
--- a/x-pack/legacy/plugins/security/public/views/management/roles_grid/components/roles_grid_page.tsx
+++ b/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx
@@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import _ from 'lodash';
+import React, { Component } from 'react';
import {
EuiButton,
EuiIcon,
@@ -18,18 +20,17 @@ import {
EuiButtonIcon,
EuiBasicTableColumn,
} from '@elastic/eui';
-import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
-import _ from 'lodash';
-import React, { Component } from 'react';
-import { toastNotifications } from 'ui/notify';
-import { Role } from '../../../../../common/model';
-import { isRoleEnabled, isReadOnlyRole, isReservedRole } from '../../../../lib/role_utils';
-import { RolesApi } from '../../../../lib/roles_api';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { NotificationsStart } from 'src/core/public';
+import { Role, isRoleEnabled, isReadOnlyRole, isReservedRole } from '../../../../common/model';
+import { RolesAPIClient } from '../roles_api_client';
import { ConfirmDelete } from './confirm_delete';
import { PermissionDenied } from './permission_denied';
interface Props {
- intl: InjectedIntl;
+ notifications: NotificationsStart;
+ rolesAPIClient: PublicMethodsOf;
}
interface State {
@@ -44,7 +45,7 @@ const getRoleManagementHref = (action: 'edit' | 'clone', roleName?: string) => {
return `#/management/security/roles/${action}${roleName ? `/${roleName}` : ''}`;
};
-class RolesGridPageUI extends Component {
+export class RolesGridPage extends Component {
constructor(props: Props) {
super(props);
this.state = {
@@ -68,7 +69,6 @@ class RolesGridPageUI extends Component {
private getPageContent = () => {
const { roles } = this.state;
- const { intl } = this.props;
return (
@@ -105,6 +105,8 @@ class RolesGridPageUI extends Component {
onCancel={this.onCancelDelete}
rolesToDelete={this.state.selection.map(role => role.name)}
callback={this.handleDelete}
+ notifications={this.props.notifications}
+ rolesAPIClient={this.props.rolesAPIClient}
/>
) : null}
@@ -112,7 +114,7 @@ class RolesGridPageUI extends Component {
!role.metadata || !role.metadata._reserved,
@@ -155,17 +157,16 @@ class RolesGridPageUI extends Component {
);
};
- private getColumnConfig = (intl: InjectedIntl) => {
- const reservedRoleDesc = intl.formatMessage({
- id: 'xpack.security.management.roles.reservedColumnDescription',
- defaultMessage: 'Reserved roles are built-in and cannot be edited or removed.',
- });
+ private getColumnConfig = () => {
+ const reservedRoleDesc = i18n.translate(
+ 'xpack.security.management.roles.reservedColumnDescription',
+ { defaultMessage: 'Reserved roles are built-in and cannot be edited or removed.' }
+ );
return [
{
field: 'name',
- name: intl.formatMessage({
- id: 'xpack.security.management.roles.nameColumnName',
+ name: i18n.translate('xpack.security.management.roles.nameColumnName', {
defaultMessage: 'Role',
}),
sortable: true,
@@ -188,8 +189,7 @@ class RolesGridPageUI extends Component {
},
{
field: 'metadata',
- name: intl.formatMessage({
- id: 'xpack.security.management.roles.reservedColumnName',
+ name: i18n.translate('xpack.security.management.roles.reservedColumnName', {
defaultMessage: 'Reserved',
}),
sortable: ({ metadata }: Role) => Boolean(metadata && metadata._reserved),
@@ -197,8 +197,7 @@ class RolesGridPageUI extends Component {
align: 'right',
description: reservedRoleDesc,
render: (metadata: Role['metadata']) => {
- const label = intl.formatMessage({
- id: 'xpack.security.management.roles.reservedRoleIconLabel',
+ const label = i18n.translate('xpack.security.management.roles.reservedRoleIconLabel', {
defaultMessage: 'Reserved role',
});
@@ -210,8 +209,7 @@ class RolesGridPageUI extends Component {
},
},
{
- name: intl.formatMessage({
- id: 'xpack.security.management.roles.actionsColumnName',
+ name: i18n.translate('xpack.security.management.roles.actionsColumnName', {
defaultMessage: 'Actions',
}),
width: '150px',
@@ -219,15 +217,10 @@ class RolesGridPageUI extends Component {
{
available: (role: Role) => !isReadOnlyRole(role),
render: (role: Role) => {
- const title = intl.formatMessage(
- {
- id: 'xpack.security.management.roles.editRoleActionName',
- defaultMessage: `Edit {roleName}`,
- },
- {
- roleName: role.name,
- }
- );
+ const title = i18n.translate('xpack.security.management.roles.editRoleActionName', {
+ defaultMessage: `Edit {roleName}`,
+ values: { roleName: role.name },
+ });
return (
{
{
available: (role: Role) => !isReservedRole(role),
render: (role: Role) => {
- const title = intl.formatMessage(
- {
- id: 'xpack.security.management.roles.cloneRoleActionName',
- defaultMessage: `Clone {roleName}`,
- },
- {
- roleName: role.name,
- }
- );
+ const title = i18n.translate('xpack.security.management.roles.cloneRoleActionName', {
+ defaultMessage: `Clone {roleName}`,
+ values: { roleName: role.name },
+ });
return (
{
private async loadRoles() {
try {
- const roles = await RolesApi.getRoles();
+ const roles = await this.props.rolesAPIClient.getRoles();
this.setState({ roles });
} catch (e) {
if (_.get(e, 'body.statusCode') === 403) {
this.setState({ permissionDenied: true });
} else {
- toastNotifications.addDanger(
- this.props.intl.formatMessage(
- {
- id: 'xpack.security.management.roles.fetchingRolesErrorMessage',
- defaultMessage: 'Error fetching roles: {message}',
- },
- { message: _.get(e, 'body.message', '') }
- )
+ this.props.notifications.toasts.addDanger(
+ i18n.translate('xpack.security.management.roles.fetchingRolesErrorMessage', {
+ defaultMessage: 'Error fetching roles: {message}',
+ values: { message: _.get(e, 'body.message', '') },
+ })
);
}
}
@@ -339,5 +324,3 @@ class RolesGridPageUI extends Component {
this.setState({ showDeleteConfirmation: false });
};
}
-
-export const RolesGridPage = injectI18n(RolesGridPageUI);
diff --git a/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx b/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx
new file mode 100644
index 0000000000000..48bc1a6580a93
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx
@@ -0,0 +1,160 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { licenseMock } from '../../../common/licensing/index.mock';
+
+jest.mock('./roles_grid', () => ({
+ RolesGridPage: (props: any) => `Roles Page: ${JSON.stringify(props)}`,
+}));
+
+jest.mock('./edit_role', () => ({
+ EditRolePage: (props: any) => `Role Edit Page: ${JSON.stringify(props)}`,
+}));
+
+import { rolesManagementApp } from './roles_management_app';
+
+import { coreMock } from '../../../../../../src/core/public/mocks';
+
+async function mountApp(basePath: string) {
+ const { fatalErrors } = coreMock.createSetup();
+ const container = document.createElement('div');
+ const setBreadcrumbs = jest.fn();
+
+ const unmount = await rolesManagementApp
+ .create({
+ license: licenseMock.create(),
+ fatalErrors,
+ getStartServices: jest.fn().mockResolvedValue([coreMock.createStart(), { data: {} }]),
+ })
+ .mount({ basePath, element: container, setBreadcrumbs });
+
+ return { unmount, container, setBreadcrumbs };
+}
+
+describe('rolesManagementApp', () => {
+ it('create() returns proper management app descriptor', () => {
+ const { fatalErrors, getStartServices } = coreMock.createSetup();
+
+ expect(
+ rolesManagementApp.create({
+ license: licenseMock.create(),
+ fatalErrors,
+ getStartServices: getStartServices as any,
+ })
+ ).toMatchInlineSnapshot(`
+ Object {
+ "id": "roles",
+ "mount": [Function],
+ "order": 20,
+ "title": "Roles",
+ }
+ `);
+ });
+
+ it('mount() works for the `grid` page', async () => {
+ const basePath = '/some-base-path/roles';
+ window.location.hash = basePath;
+
+ const { setBreadcrumbs, container, unmount } = await mountApp(basePath);
+
+ expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
+ expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `#${basePath}`, text: 'Roles' }]);
+ expect(container).toMatchInlineSnapshot(`
+
+ Roles Page: {"notifications":{"toasts":{}},"rolesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}}}
+
+ `);
+
+ unmount();
+
+ expect(container).toMatchInlineSnapshot(``);
+ });
+
+ it('mount() works for the `create role` page', async () => {
+ const basePath = '/some-base-path/roles';
+ window.location.hash = `${basePath}/edit`;
+
+ const { setBreadcrumbs, container, unmount } = await mountApp(basePath);
+
+ expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
+ expect(setBreadcrumbs).toHaveBeenCalledWith([
+ { href: `#${basePath}`, text: 'Roles' },
+ { text: 'Create' },
+ ]);
+ expect(container).toMatchInlineSnapshot(`
+
+ Role Edit Page: {"action":"edit","rolesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"userAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"http":{"basePath":{"basePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}}}
+
+ `);
+
+ unmount();
+
+ expect(container).toMatchInlineSnapshot(``);
+ });
+
+ it('mount() works for the `edit role` page', async () => {
+ const basePath = '/some-base-path/roles';
+ const roleName = 'someRoleName';
+ window.location.hash = `${basePath}/edit/${roleName}`;
+
+ const { setBreadcrumbs, container, unmount } = await mountApp(basePath);
+
+ expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
+ expect(setBreadcrumbs).toHaveBeenCalledWith([
+ { href: `#${basePath}`, text: 'Roles' },
+ { href: `#/some-base-path/roles/edit/${roleName}`, text: roleName },
+ ]);
+ expect(container).toMatchInlineSnapshot(`
+
+ Role Edit Page: {"action":"edit","roleName":"someRoleName","rolesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"userAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"http":{"basePath":{"basePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}}}
+
+ `);
+
+ unmount();
+
+ expect(container).toMatchInlineSnapshot(``);
+ });
+
+ it('mount() works for the `clone role` page', async () => {
+ const basePath = '/some-base-path/roles';
+ const roleName = 'someRoleName';
+ window.location.hash = `${basePath}/clone/${roleName}`;
+
+ const { setBreadcrumbs, container, unmount } = await mountApp(basePath);
+
+ expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
+ expect(setBreadcrumbs).toHaveBeenCalledWith([
+ { href: `#${basePath}`, text: 'Roles' },
+ { text: 'Create' },
+ ]);
+ expect(container).toMatchInlineSnapshot(`
+
+ Role Edit Page: {"action":"clone","roleName":"someRoleName","rolesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"userAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"http":{"basePath":{"basePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}}}
+
+ `);
+
+ unmount();
+
+ expect(container).toMatchInlineSnapshot(``);
+ });
+
+ it('mount() properly encodes role name in `edit role` page link in breadcrumbs', async () => {
+ const basePath = '/some-base-path/roles';
+ const roleName = 'some 安全性 role';
+ window.location.hash = `${basePath}/edit/${roleName}`;
+
+ const { setBreadcrumbs } = await mountApp(basePath);
+
+ expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
+ expect(setBreadcrumbs).toHaveBeenCalledWith([
+ { href: `#${basePath}`, text: 'Roles' },
+ {
+ href: '#/some-base-path/roles/edit/some%20%E5%AE%89%E5%85%A8%E6%80%A7%20role',
+ text: roleName,
+ },
+ ]);
+ });
+});
diff --git a/x-pack/plugins/security/public/management/roles/roles_management_app.tsx b/x-pack/plugins/security/public/management/roles/roles_management_app.tsx
new file mode 100644
index 0000000000000..4e8c95b61c2f1
--- /dev/null
+++ b/x-pack/plugins/security/public/management/roles/roles_management_app.tsx
@@ -0,0 +1,116 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { render, unmountComponentAtNode } from 'react-dom';
+import { HashRouter as Router, Route, Switch, useParams } from 'react-router-dom';
+import { i18n } from '@kbn/i18n';
+import { CoreSetup, FatalErrorsSetup } from 'src/core/public';
+import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public';
+import { SecurityLicense } from '../../../common/licensing';
+import { PluginStartDependencies } from '../../plugin';
+import { UserAPIClient } from '../users';
+import { RolesAPIClient } from './roles_api_client';
+import { RolesGridPage } from './roles_grid';
+import { EditRolePage } from './edit_role';
+import { DocumentationLinksService } from './documentation_links';
+import { IndicesAPIClient } from './indices_api_client';
+import { PrivilegesAPIClient } from './privileges_api_client';
+
+interface CreateParams {
+ fatalErrors: FatalErrorsSetup;
+ license: SecurityLicense;
+ getStartServices: CoreSetup['getStartServices'];
+}
+
+export const rolesManagementApp = Object.freeze({
+ id: 'roles',
+ create({ license, fatalErrors, getStartServices }: CreateParams) {
+ return {
+ id: this.id,
+ order: 20,
+ title: i18n.translate('xpack.security.management.rolesTitle', { defaultMessage: 'Roles' }),
+ async mount({ basePath, element, setBreadcrumbs }) {
+ const [
+ { application, docLinks, http, i18n: i18nStart, injectedMetadata, notifications },
+ { data },
+ ] = await getStartServices();
+
+ const rolesBreadcrumbs = [
+ {
+ text: i18n.translate('xpack.security.roles.breadcrumb', { defaultMessage: 'Roles' }),
+ href: `#${basePath}`,
+ },
+ ];
+
+ const rolesAPIClient = new RolesAPIClient(http);
+ const RolesGridPageWithBreadcrumbs = () => {
+ setBreadcrumbs(rolesBreadcrumbs);
+ return ;
+ };
+
+ const EditRolePageWithBreadcrumbs = ({ action }: { action: 'edit' | 'clone' }) => {
+ const { roleName } = useParams<{ roleName?: string }>();
+
+ setBreadcrumbs([
+ ...rolesBreadcrumbs,
+ action === 'edit' && roleName
+ ? { text: roleName, href: `#${basePath}/edit/${encodeURIComponent(roleName)}` }
+ : {
+ text: i18n.translate('xpack.security.roles.createBreadcrumb', {
+ defaultMessage: 'Create',
+ }),
+ },
+ ]);
+
+ return (
+
+ );
+ };
+
+ render(
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ element
+ );
+
+ return () => {
+ unmountComponentAtNode(element);
+ };
+ },
+ } as RegisterManagementAppArgs;
+ },
+});
diff --git a/x-pack/plugins/security/public/management/users/_index.scss b/x-pack/plugins/security/public/management/users/_index.scss
new file mode 100644
index 0000000000000..35df0c1b96583
--- /dev/null
+++ b/x-pack/plugins/security/public/management/users/_index.scss
@@ -0,0 +1 @@
+@import './edit_user/index';
diff --git a/x-pack/legacy/plugins/security/public/components/management/change_password_form/change_password_form.test.tsx b/x-pack/plugins/security/public/management/users/components/change_password_form/change_password_form.test.tsx
similarity index 82%
rename from x-pack/legacy/plugins/security/public/components/management/change_password_form/change_password_form.test.tsx
rename to x-pack/plugins/security/public/management/users/components/change_password_form/change_password_form.test.tsx
index 221120532318c..be46612767a59 100644
--- a/x-pack/legacy/plugins/security/public/components/management/change_password_form/change_password_form.test.tsx
+++ b/x-pack/plugins/security/public/management/users/components/change_password_form/change_password_form.test.tsx
@@ -7,10 +7,12 @@ import { EuiFieldText } from '@elastic/eui';
import { ReactWrapper } from 'enzyme';
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
-import { User } from '../../../../common/model';
-import { UserAPIClient } from '../../../lib/api';
+import { User } from '../../../../../common/model';
import { ChangePasswordForm } from './change_password_form';
+import { coreMock } from '../../../../../../../../src/core/public/mocks';
+import { userAPIClientMock } from '../../index.mock';
+
function getCurrentPasswordField(wrapper: ReactWrapper) {
return wrapper.find(EuiFieldText).filter('[data-test-subj="currentPassword"]');
}
@@ -23,8 +25,6 @@ function getConfirmPasswordField(wrapper: ReactWrapper) {
return wrapper.find(EuiFieldText).filter('[data-test-subj="confirmNewPassword"]');
}
-jest.mock('ui/kfetch');
-
describe('', () => {
describe('for the current user', () => {
it('shows fields for current and new passwords', () => {
@@ -40,7 +40,8 @@ describe('', () => {
);
@@ -60,15 +61,15 @@ describe('', () => {
const callback = jest.fn();
- const apiClient = new UserAPIClient();
- apiClient.changePassword = jest.fn();
+ const apiClientMock = userAPIClientMock.create();
const wrapper = mountWithIntl(
);
@@ -83,8 +84,8 @@ describe('', () => {
wrapper.find('button[data-test-subj="changePasswordButton"]').simulate('click');
- expect(apiClient.changePassword).toHaveBeenCalledTimes(1);
- expect(apiClient.changePassword).toHaveBeenCalledWith(
+ expect(apiClientMock.changePassword).toHaveBeenCalledTimes(1);
+ expect(apiClientMock.changePassword).toHaveBeenCalledWith(
'user',
'myNewPassword',
'myCurrentPassword'
@@ -106,7 +107,8 @@ describe('', () => {
);
diff --git a/x-pack/legacy/plugins/security/public/components/management/change_password_form/change_password_form.tsx b/x-pack/plugins/security/public/management/users/components/change_password_form/change_password_form.tsx
similarity index 96%
rename from x-pack/legacy/plugins/security/public/components/management/change_password_form/change_password_form.tsx
rename to x-pack/plugins/security/public/management/users/components/change_password_form/change_password_form.tsx
index 61c0b77decd56..6dcf330ec6f9e 100644
--- a/x-pack/legacy/plugins/security/public/components/management/change_password_form/change_password_form.tsx
+++ b/x-pack/plugins/security/public/management/users/components/change_password_form/change_password_form.tsx
@@ -5,10 +5,7 @@
*/
import {
EuiButton,
- // @ts-ignore
EuiButtonEmpty,
- // @ts-ignore
- EuiDescribedFormGroup,
EuiFieldText,
EuiFlexGroup,
EuiFlexItem,
@@ -18,15 +15,16 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { ChangeEvent, Component } from 'react';
-import { toastNotifications } from 'ui/notify';
-import { User } from '../../../../common/model';
-import { UserAPIClient } from '../../../lib/api';
+import { NotificationsStart } from 'src/core/public';
+import { User } from '../../../../../common/model';
+import { UserAPIClient } from '../..';
interface Props {
user: User;
isUserChangingOwnPassword: boolean;
onChangePassword?: () => void;
- apiClient: UserAPIClient;
+ apiClient: PublicMethodsOf;
+ notifications: NotificationsStart;
}
interface State {
@@ -294,7 +292,7 @@ export class ChangePasswordForm extends Component {
};
private handleChangePasswordSuccess = () => {
- toastNotifications.addSuccess({
+ this.props.notifications.toasts.addSuccess({
title: i18n.translate('xpack.security.account.changePasswordSuccess', {
defaultMessage: 'Your password has been changed.',
}),
@@ -317,7 +315,7 @@ export class ChangePasswordForm extends Component {
if (error.body && error.body.statusCode === 403) {
this.setState({ currentPasswordError: true });
} else {
- toastNotifications.addDanger(
+ this.props.notifications.toasts.addDanger(
i18n.translate('xpack.security.management.users.editUser.settingPasswordErrorMessage', {
defaultMessage: 'Error setting password: {message}',
values: { message: _.get(error, 'body.message') },
diff --git a/x-pack/legacy/plugins/security/public/components/management/change_password_form/index.ts b/x-pack/plugins/security/public/management/users/components/change_password_form/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/components/management/change_password_form/index.ts
rename to x-pack/plugins/security/public/management/users/components/change_password_form/index.ts
diff --git a/x-pack/legacy/plugins/security/public/components/management/users/confirm_delete.test.tsx b/x-pack/plugins/security/public/management/users/components/confirm_delete_users/confirm_delete_users.test.tsx
similarity index 58%
rename from x-pack/legacy/plugins/security/public/components/management/users/confirm_delete.test.tsx
rename to x-pack/plugins/security/public/management/users/components/confirm_delete_users/confirm_delete_users.test.tsx
index 9f69fc7a7551f..bcec707b03f93 100644
--- a/x-pack/legacy/plugins/security/public/components/management/users/confirm_delete.test.tsx
+++ b/x-pack/plugins/security/public/management/users/components/confirm_delete_users/confirm_delete_users.test.tsx
@@ -5,16 +5,21 @@
*/
import { mountWithIntl } from 'test_utils/enzyme_helpers';
-import { ConfirmDeleteUsers } from './confirm_delete';
+import { ConfirmDeleteUsers } from './confirm_delete_users';
import React from 'react';
-import { UserAPIClient } from '../../../lib/api';
-jest.mock('ui/kfetch');
+import { coreMock } from '../../../../../../../../src/core/public/mocks';
+import { userAPIClientMock } from '../../index.mock';
describe('ConfirmDeleteUsers', () => {
it('renders a warning for a single user', () => {
const wrapper = mountWithIntl(
-
+
);
expect(wrapper.find('EuiModalHeaderTitle').text()).toMatchInlineSnapshot(`"Delete user foo"`);
@@ -23,7 +28,8 @@ describe('ConfirmDeleteUsers', () => {
it('renders a warning for a multiple users', () => {
const wrapper = mountWithIntl(
@@ -35,7 +41,12 @@ describe('ConfirmDeleteUsers', () => {
it('fires onCancel when the operation is cancelled', () => {
const onCancel = jest.fn();
const wrapper = mountWithIntl(
-
+
);
expect(onCancel).toBeCalledTimes(0);
@@ -47,50 +58,48 @@ describe('ConfirmDeleteUsers', () => {
it('deletes the requested users when confirmed', () => {
const onCancel = jest.fn();
- const deleteUser = jest.fn();
-
- const apiClient = new UserAPIClient();
- apiClient.deleteUser = deleteUser;
+ const apiClientMock = userAPIClientMock.create();
const wrapper = mountWithIntl(
);
wrapper.find('EuiButton[data-test-subj="confirmModalConfirmButton"]').simulate('click');
- expect(deleteUser).toBeCalledTimes(2);
- expect(deleteUser).toBeCalledWith('foo');
- expect(deleteUser).toBeCalledWith('bar');
+ expect(apiClientMock.deleteUser).toBeCalledTimes(2);
+ expect(apiClientMock.deleteUser).toBeCalledWith('foo');
+ expect(apiClientMock.deleteUser).toBeCalledWith('bar');
});
it('attempts to delete all users even if some fail', () => {
const onCancel = jest.fn();
- const deleteUser = jest.fn().mockImplementation(user => {
+
+ const apiClientMock = userAPIClientMock.create();
+ apiClientMock.deleteUser.mockImplementation(user => {
if (user === 'foo') {
return Promise.reject('something terrible happened');
}
return Promise.resolve();
});
- const apiClient = new UserAPIClient();
- apiClient.deleteUser = deleteUser;
-
const wrapper = mountWithIntl(
);
wrapper.find('EuiButton[data-test-subj="confirmModalConfirmButton"]').simulate('click');
- expect(deleteUser).toBeCalledTimes(2);
- expect(deleteUser).toBeCalledWith('foo');
- expect(deleteUser).toBeCalledWith('bar');
+ expect(apiClientMock.deleteUser).toBeCalledTimes(2);
+ expect(apiClientMock.deleteUser).toBeCalledWith('foo');
+ expect(apiClientMock.deleteUser).toBeCalledWith('bar');
});
});
diff --git a/x-pack/legacy/plugins/security/public/components/management/users/confirm_delete.tsx b/x-pack/plugins/security/public/management/users/components/confirm_delete_users/confirm_delete_users.tsx
similarity index 50%
rename from x-pack/legacy/plugins/security/public/components/management/users/confirm_delete.tsx
rename to x-pack/plugins/security/public/management/users/components/confirm_delete_users/confirm_delete_users.tsx
index 53bb022afb513..b7269e0168d7d 100644
--- a/x-pack/legacy/plugins/security/public/components/management/users/confirm_delete.tsx
+++ b/x-pack/plugins/security/public/management/users/components/confirm_delete_users/confirm_delete_users.tsx
@@ -6,51 +6,46 @@
import React, { Component, Fragment } from 'react';
import { EuiOverlayMask, EuiConfirmModal } from '@elastic/eui';
-import { toastNotifications } from 'ui/notify';
-import { FormattedMessage, injectI18n, InjectedIntl } from '@kbn/i18n/react';
-import { UserAPIClient } from '../../../lib/api';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { NotificationsStart } from 'src/core/public';
+import { UserAPIClient } from '../..';
interface Props {
- intl: InjectedIntl;
usersToDelete: string[];
- apiClient: UserAPIClient;
+ apiClient: PublicMethodsOf;
+ notifications: NotificationsStart;
onCancel: () => void;
callback?: (usersToDelete: string[], errors: string[]) => void;
}
-class ConfirmDeleteUI extends Component {
+export class ConfirmDeleteUsers extends Component {
public render() {
- const { usersToDelete, onCancel, intl } = this.props;
+ const { usersToDelete, onCancel } = this.props;
const moreThanOne = usersToDelete.length > 1;
const title = moreThanOne
- ? intl.formatMessage(
- {
- id: 'xpack.security.management.users.confirmDelete.deleteMultipleUsersTitle',
- defaultMessage: 'Delete {userLength} users',
- },
- { userLength: usersToDelete.length }
- )
- : intl.formatMessage(
- {
- id: 'xpack.security.management.users.confirmDelete.deleteOneUserTitle',
- defaultMessage: 'Delete user {userLength}',
- },
- { userLength: usersToDelete[0] }
- );
+ ? i18n.translate('xpack.security.management.users.confirmDelete.deleteMultipleUsersTitle', {
+ defaultMessage: 'Delete {userLength} users',
+ values: { userLength: usersToDelete.length },
+ })
+ : i18n.translate('xpack.security.management.users.confirmDelete.deleteOneUserTitle', {
+ defaultMessage: 'Delete user {userLength}',
+ values: { userLength: usersToDelete[0] },
+ });
return (
@@ -82,31 +77,23 @@ class ConfirmDeleteUI extends Component
{
}
private deleteUsers = () => {
- const { usersToDelete, callback, apiClient } = this.props;
+ const { usersToDelete, callback, apiClient, notifications } = this.props;
const errors: string[] = [];
usersToDelete.forEach(async username => {
try {
await apiClient.deleteUser(username);
- toastNotifications.addSuccess(
- this.props.intl.formatMessage(
- {
- id:
- 'xpack.security.management.users.confirmDelete.userSuccessfullyDeletedNotificationMessage',
- defaultMessage: 'Deleted user {username}',
- },
- { username }
+ notifications.toasts.addSuccess(
+ i18n.translate(
+ 'xpack.security.management.users.confirmDelete.userSuccessfullyDeletedNotificationMessage',
+ { defaultMessage: 'Deleted user {username}', values: { username } }
)
);
} catch (e) {
errors.push(username);
- toastNotifications.addDanger(
- this.props.intl.formatMessage(
- {
- id:
- 'xpack.security.management.users.confirmDelete.userDeletingErrorNotificationMessage',
- defaultMessage: 'Error deleting user {username}',
- },
- { username }
+ notifications.toasts.addDanger(
+ i18n.translate(
+ 'xpack.security.management.users.confirmDelete.userDeletingErrorNotificationMessage',
+ { defaultMessage: 'Error deleting user {username}', values: { username } }
)
);
}
@@ -116,5 +103,3 @@ class ConfirmDeleteUI extends Component {
});
};
}
-
-export const ConfirmDeleteUsers = injectI18n(ConfirmDeleteUI);
diff --git a/x-pack/legacy/plugins/security/public/components/management/users/index.ts b/x-pack/plugins/security/public/management/users/components/confirm_delete_users/index.ts
similarity index 79%
rename from x-pack/legacy/plugins/security/public/components/management/users/index.ts
rename to x-pack/plugins/security/public/management/users/components/confirm_delete_users/index.ts
index 813e671c05ccf..fde35ab0f0d02 100644
--- a/x-pack/legacy/plugins/security/public/components/management/users/index.ts
+++ b/x-pack/plugins/security/public/management/users/components/confirm_delete_users/index.ts
@@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export { ConfirmDeleteUsers } from './confirm_delete';
+export { ConfirmDeleteUsers } from './confirm_delete_users';
diff --git a/x-pack/plugins/security/public/management/users/components/index.ts b/x-pack/plugins/security/public/management/users/components/index.ts
new file mode 100644
index 0000000000000..54011a6a24cbd
--- /dev/null
+++ b/x-pack/plugins/security/public/management/users/components/index.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export { ChangePasswordForm } from './change_password_form';
+export { ConfirmDeleteUsers } from './confirm_delete_users';
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_user/_users.scss b/x-pack/plugins/security/public/management/users/edit_user/_edit_user_page.scss
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_user/_users.scss
rename to x-pack/plugins/security/public/management/users/edit_user/_edit_user_page.scss
diff --git a/x-pack/plugins/security/public/management/users/edit_user/_index.scss b/x-pack/plugins/security/public/management/users/edit_user/_index.scss
new file mode 100644
index 0000000000000..734ba7882ba72
--- /dev/null
+++ b/x-pack/plugins/security/public/management/users/edit_user/_index.scss
@@ -0,0 +1 @@
+@import './edit_user_page';
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_user/components/edit_user_page.test.tsx b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx
similarity index 70%
rename from x-pack/legacy/plugins/security/public/views/management/edit_user/components/edit_user_page.test.tsx
rename to x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx
index 639646ce48e22..543d20bb92afe 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_user/components/edit_user_page.test.tsx
+++ b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx
@@ -8,13 +8,14 @@ import { act } from '@testing-library/react';
import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers';
import { EditUserPage } from './edit_user_page';
import React from 'react';
-import { securityMock } from '../../../../../../../../plugins/security/public/mocks';
-import { UserAPIClient } from '../../../../lib/api';
-import { User, Role } from '../../../../../common/model';
+import { User, Role } from '../../../../common/model';
import { ReactWrapper } from 'enzyme';
-import { mockAuthenticatedUser } from '../../../../../../../../plugins/security/common/model/authenticated_user.mock';
-jest.mock('ui/kfetch');
+import { coreMock } from '../../../../../../../src/core/public/mocks';
+import { mockAuthenticatedUser } from '../../../../common/model/authenticated_user.mock';
+import { securityMock } from '../../../mocks';
+import { rolesAPIClientMock } from '../../roles/index.mock';
+import { userAPIClientMock } from '../index.mock';
const createUser = (username: string) => {
const user: User = {
@@ -34,14 +35,12 @@ const createUser = (username: string) => {
return user;
};
-const buildClient = () => {
- const apiClient = new UserAPIClient();
+const buildClients = () => {
+ const apiClient = userAPIClientMock.create();
+ apiClient.getUser.mockImplementation(async (username: string) => createUser(username));
- apiClient.getUser = jest
- .fn()
- .mockImplementation(async (username: string) => createUser(username));
-
- apiClient.getRoles = jest.fn().mockImplementation(() => {
+ const rolesAPIClient = rolesAPIClientMock.create();
+ rolesAPIClient.getRoles.mockImplementation(() => {
return Promise.resolve([
{
name: 'role 1',
@@ -64,7 +63,7 @@ const buildClient = () => {
] as Role[]);
});
- return apiClient;
+ return { apiClient, rolesAPIClient };
};
function buildSecuritySetup() {
@@ -85,15 +84,15 @@ function expectMissingSaveButton(wrapper: ReactWrapper) {
describe('EditUserPage', () => {
it('allows reserved users to be viewed', async () => {
- const apiClient = buildClient();
+ const { apiClient, rolesAPIClient } = buildClients();
const securitySetup = buildSecuritySetup();
const wrapper = mountWithIntl(
- path}
- intl={null as any}
+ rolesAPIClient={rolesAPIClient}
+ authc={securitySetup.authc}
+ notifications={coreMock.createStart().notifications}
/>
);
@@ -106,15 +105,15 @@ describe('EditUserPage', () => {
});
it('allows new users to be created', async () => {
- const apiClient = buildClient();
+ const { apiClient, rolesAPIClient } = buildClients();
const securitySetup = buildSecuritySetup();
const wrapper = mountWithIntl(
- path}
- intl={null as any}
+ rolesAPIClient={rolesAPIClient}
+ authc={securitySetup.authc}
+ notifications={coreMock.createStart().notifications}
/>
);
@@ -127,15 +126,15 @@ describe('EditUserPage', () => {
});
it('allows existing users to be edited', async () => {
- const apiClient = buildClient();
+ const { apiClient, rolesAPIClient } = buildClients();
const securitySetup = buildSecuritySetup();
const wrapper = mountWithIntl(
- path}
- intl={null as any}
+ rolesAPIClient={rolesAPIClient}
+ authc={securitySetup.authc}
+ notifications={coreMock.createStart().notifications}
/>
);
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_user/components/edit_user_page.tsx b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx
similarity index 77%
rename from x-pack/legacy/plugins/security/public/views/management/edit_user/components/edit_user_page.tsx
rename to x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx
index bbffe07485f8d..576f3ff9e6008 100644
--- a/x-pack/legacy/plugins/security/public/views/management/edit_user/components/edit_user_page.tsx
+++ b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx
@@ -26,22 +26,23 @@ import {
EuiHorizontalRule,
EuiSpacer,
} from '@elastic/eui';
-import { toastNotifications } from 'ui/notify';
-import { FormattedMessage, injectI18n, InjectedIntl } from '@kbn/i18n/react';
-import { SecurityPluginSetup } from '../../../../../../../../plugins/security/public';
-import { UserValidator, UserValidationResult } from '../../../../lib/validate_user';
-import { User, EditUser, Role } from '../../../../../common/model';
-import { USERS_PATH } from '../../../../views/management/management_urls';
-import { ConfirmDeleteUsers } from '../../../../components/management/users';
-import { UserAPIClient } from '../../../../lib/api';
-import { ChangePasswordForm } from '../../../../components/management/change_password_form';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { NotificationsStart } from 'src/core/public';
+import { User, EditUser, Role } from '../../../../common/model';
+import { AuthenticationServiceSetup } from '../../../authentication';
+import { USERS_PATH } from '../../management_urls';
+import { RolesAPIClient } from '../../roles';
+import { ConfirmDeleteUsers, ChangePasswordForm } from '../components';
+import { UserValidator, UserValidationResult } from './validate_user';
+import { UserAPIClient } from '..';
interface Props {
- username: string;
- intl: InjectedIntl;
- changeUrl: (path: string) => void;
- apiClient: UserAPIClient;
- securitySetup: SecurityPluginSetup;
+ username?: string;
+ apiClient: PublicMethodsOf;
+ rolesAPIClient: PublicMethodsOf;
+ authc: AuthenticationServiceSetup;
+ notifications: NotificationsStart;
}
interface State {
@@ -56,7 +57,11 @@ interface State {
formError: UserValidationResult | null;
}
-class EditUserPageUI extends Component {
+function backToUserList() {
+ window.location.hash = USERS_PATH;
+}
+
+export class EditUserPage extends Component {
private validator: UserValidator;
constructor(props: Props) {
@@ -84,7 +89,17 @@ class EditUserPageUI extends Component {
}
public async componentDidMount() {
- const { username, apiClient, securitySetup } = this.props;
+ await this.setCurrentUser();
+ }
+
+ public async componentDidUpdate(prevProps: Props) {
+ if (prevProps.username !== this.props.username) {
+ await this.setCurrentUser();
+ }
+ }
+
+ private async setCurrentUser() {
+ const { username, apiClient, rolesAPIClient, notifications, authc } = this.props;
let { user, currentUser } = this.state;
if (username) {
try {
@@ -93,26 +108,24 @@ class EditUserPageUI extends Component {
password: '',
confirmPassword: '',
};
- currentUser = await securitySetup.authc.getCurrentUser();
+ currentUser = await authc.getCurrentUser();
} catch (err) {
- toastNotifications.addDanger({
- title: this.props.intl.formatMessage({
- id: 'xpack.security.management.users.editUser.errorLoadingUserTitle',
+ notifications.toasts.addDanger({
+ title: i18n.translate('xpack.security.management.users.editUser.errorLoadingUserTitle', {
defaultMessage: 'Error loading user',
}),
text: get(err, 'body.message') || err.message,
});
- return;
+ return backToUserList();
}
}
let roles: Role[] = [];
try {
- roles = await apiClient.getRoles();
+ roles = await rolesAPIClient.getRoles();
} catch (err) {
- toastNotifications.addDanger({
- title: this.props.intl.formatMessage({
- id: 'xpack.security.management.users.editUser.errorLoadingRolesTitle',
+ notifications.toasts.addDanger({
+ title: i18n.translate('xpack.security.management.users.editUser.errorLoadingRolesTitle', {
defaultMessage: 'Error loading roles',
}),
text: get(err, 'body.message') || err.message,
@@ -131,8 +144,7 @@ class EditUserPageUI extends Component {
private handleDelete = (usernames: string[], errors: string[]) => {
if (errors.length === 0) {
- const { changeUrl } = this.props;
- changeUrl(USERS_PATH);
+ backToUserList();
}
};
@@ -148,7 +160,7 @@ class EditUserPageUI extends Component {
this.setState({
formError: null,
});
- const { changeUrl, apiClient } = this.props;
+ const { apiClient } = this.props;
const { user, isNewUser, selectedRoles } = this.state;
const userToSave: EditUser = { ...user };
if (!isNewUser) {
@@ -160,26 +172,23 @@ class EditUserPageUI extends Component {
});
try {
await apiClient.saveUser(userToSave);
- toastNotifications.addSuccess(
- this.props.intl.formatMessage(
+ this.props.notifications.toasts.addSuccess(
+ i18n.translate(
+ 'xpack.security.management.users.editUser.userSuccessfullySavedNotificationMessage',
{
- id:
- 'xpack.security.management.users.editUser.userSuccessfullySavedNotificationMessage',
defaultMessage: 'Saved user {message}',
- },
- { message: user.username }
+ values: { message: user.username },
+ }
)
);
- changeUrl(USERS_PATH);
+
+ backToUserList();
} catch (e) {
- toastNotifications.addDanger(
- this.props.intl.formatMessage(
- {
- id: 'xpack.security.management.users.editUser.savingUserErrorMessage',
- defaultMessage: 'Error saving user: {message}',
- },
- { message: get(e, 'body.message', 'Unknown error') }
- )
+ this.props.notifications.toasts.addDanger(
+ i18n.translate('xpack.security.management.users.editUser.savingUserErrorMessage', {
+ defaultMessage: 'Error saving user: {message}',
+ values: { message: get(e, 'body.message', 'Unknown error') },
+ })
);
}
}
@@ -189,8 +198,7 @@ class EditUserPageUI extends Component {
return (
{
/>
{
{user.username === 'kibana' ? (
@@ -260,6 +268,7 @@ class EditUserPageUI extends Component {
isUserChangingOwnPassword={userIsLoggedInUser}
onChangePassword={this.toggleChangePasswordForm}
apiClient={this.props.apiClient}
+ notifications={this.props.notifications}
/>
);
@@ -352,7 +361,6 @@ class EditUserPageUI extends Component {
};
public render() {
- const { changeUrl, intl } = this.props;
const {
user,
roles,
@@ -417,6 +425,7 @@ class EditUserPageUI extends Component {
usersToDelete={[user.username]}
callback={this.handleDelete}
apiClient={this.props.apiClient}
+ notifications={this.props.notifications}
/>
) : null}
@@ -425,17 +434,16 @@ class EditUserPageUI extends Component {
{...this.validator.validateUsername(this.state.user)}
helpText={
!isNewUser && !reserved
- ? intl.formatMessage({
- id:
- 'xpack.security.management.users.editUser.changingUserNameAfterCreationDescription',
- defaultMessage: `Usernames can't be changed after creation.`,
- })
+ ? i18n.translate(
+ 'xpack.security.management.users.editUser.changingUserNameAfterCreationDescription',
+ { defaultMessage: `Usernames can't be changed after creation.` }
+ )
: null
}
- label={intl.formatMessage({
- id: 'xpack.security.management.users.editUser.usernameFormRowLabel',
- defaultMessage: 'Username',
- })}
+ label={i18n.translate(
+ 'xpack.security.management.users.editUser.usernameFormRowLabel',
+ { defaultMessage: 'Username' }
+ )}
>
{
{reserved ? null : (
{
{
)}
{
@@ -513,7 +521,7 @@ class EditUserPageUI extends Component {
{reserved && (
- changeUrl(USERS_PATH)}>
+
{
- changeUrl(USERS_PATH)}
- >
+
{
);
}
}
-
-export const EditUserPage = injectI18n(EditUserPageUI);
diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_user/components/index.ts b/x-pack/plugins/security/public/management/users/edit_user/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/security/public/views/management/edit_user/components/index.ts
rename to x-pack/plugins/security/public/management/users/edit_user/index.ts
diff --git a/x-pack/legacy/plugins/security/public/lib/validate_user.test.ts b/x-pack/plugins/security/public/management/users/edit_user/validate_user.test.ts
similarity index 98%
rename from x-pack/legacy/plugins/security/public/lib/validate_user.test.ts
rename to x-pack/plugins/security/public/management/users/edit_user/validate_user.test.ts
index 0535248fede88..6050e1868a759 100644
--- a/x-pack/legacy/plugins/security/public/lib/validate_user.test.ts
+++ b/x-pack/plugins/security/public/management/users/edit_user/validate_user.test.ts
@@ -5,7 +5,7 @@
*/
import { UserValidator, UserValidationResult } from './validate_user';
-import { User, EditUser } from '../../common/model';
+import { User, EditUser } from '../../../../common/model';
function expectValid(result: UserValidationResult) {
expect(result.isInvalid).toBe(false);
diff --git a/x-pack/legacy/plugins/security/public/lib/validate_user.ts b/x-pack/plugins/security/public/management/users/edit_user/validate_user.ts
similarity index 98%
rename from x-pack/legacy/plugins/security/public/lib/validate_user.ts
rename to x-pack/plugins/security/public/management/users/edit_user/validate_user.ts
index 113aaacdcbf96..5edd96c68bf0d 100644
--- a/x-pack/legacy/plugins/security/public/lib/validate_user.ts
+++ b/x-pack/plugins/security/public/management/users/edit_user/validate_user.ts
@@ -5,7 +5,7 @@
*/
import { i18n } from '@kbn/i18n';
-import { User, EditUser } from '../../common/model';
+import { User, EditUser } from '../../../../common/model';
interface UserValidatorOptions {
shouldValidate?: boolean;
diff --git a/x-pack/legacy/plugins/security/public/views/management/index.js b/x-pack/plugins/security/public/management/users/index.mock.ts
similarity index 80%
rename from x-pack/legacy/plugins/security/public/views/management/index.js
rename to x-pack/plugins/security/public/management/users/index.mock.ts
index 0ed6fe09ef80a..f090f88da500d 100644
--- a/x-pack/legacy/plugins/security/public/views/management/index.js
+++ b/x-pack/plugins/security/public/management/users/index.mock.ts
@@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import './management';
+export { userAPIClientMock } from './user_api_client.mock';
diff --git a/x-pack/legacy/plugins/security/public/objects/index.ts b/x-pack/plugins/security/public/management/users/index.ts
similarity index 68%
rename from x-pack/legacy/plugins/security/public/objects/index.ts
rename to x-pack/plugins/security/public/management/users/index.ts
index a6238ca879901..c8b4d41973da6 100644
--- a/x-pack/legacy/plugins/security/public/objects/index.ts
+++ b/x-pack/plugins/security/public/management/users/index.ts
@@ -4,6 +4,5 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export { saveRole, deleteRole } from './lib/roles';
-
-export { getFields } from './lib/get_fields';
+export { UserAPIClient } from './user_api_client';
+export { usersManagementApp } from './users_management_app';
diff --git a/x-pack/plugins/security/public/management/users/user_api_client.mock.ts b/x-pack/plugins/security/public/management/users/user_api_client.mock.ts
new file mode 100644
index 0000000000000..7223f78d57fdc
--- /dev/null
+++ b/x-pack/plugins/security/public/management/users/user_api_client.mock.ts
@@ -0,0 +1,15 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export const userAPIClientMock = {
+ create: () => ({
+ getUsers: jest.fn(),
+ getUser: jest.fn(),
+ deleteUser: jest.fn(),
+ saveUser: jest.fn(),
+ changePassword: jest.fn(),
+ }),
+};
diff --git a/x-pack/plugins/security/public/management/users/user_api_client.ts b/x-pack/plugins/security/public/management/users/user_api_client.ts
new file mode 100644
index 0000000000000..61dd09d2c5e3d
--- /dev/null
+++ b/x-pack/plugins/security/public/management/users/user_api_client.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { HttpStart } from 'src/core/public';
+import { User, EditUser } from '../../../common/model';
+
+const usersUrl = '/internal/security/users';
+
+export class UserAPIClient {
+ constructor(private readonly http: HttpStart) {}
+
+ public async getUsers() {
+ return await this.http.get(usersUrl);
+ }
+
+ public async getUser(username: string) {
+ return await this.http.get(`${usersUrl}/${encodeURIComponent(username)}`);
+ }
+
+ public async deleteUser(username: string) {
+ await this.http.delete(`${usersUrl}/${encodeURIComponent(username)}`);
+ }
+
+ public async saveUser(user: EditUser) {
+ await this.http.post(`${usersUrl}/${encodeURIComponent(user.username)}`, {
+ body: JSON.stringify(user),
+ });
+ }
+
+ public async changePassword(username: string, password: string, currentPassword: string) {
+ const data: Record = {
+ newPassword: password,
+ };
+ if (currentPassword) {
+ data.password = currentPassword;
+ }
+
+ await this.http.post(`${usersUrl}/${encodeURIComponent(username)}/password`, {
+ body: JSON.stringify(data),
+ });
+ }
+}
diff --git a/x-pack/legacy/plugins/security/public/views/management/users_grid/components/index.ts b/x-pack/plugins/security/public/management/users/users_grid/index.ts
similarity index 82%
rename from x-pack/legacy/plugins/security/public/views/management/users_grid/components/index.ts
rename to x-pack/plugins/security/public/management/users/users_grid/index.ts
index 03721f5ce93b1..90e16248e19c3 100644
--- a/x-pack/legacy/plugins/security/public/views/management/users_grid/components/index.ts
+++ b/x-pack/plugins/security/public/management/users/users_grid/index.ts
@@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export { UsersListPage } from './users_list_page';
+export { UsersGridPage } from './users_grid_page';
diff --git a/x-pack/legacy/plugins/security/public/views/management/users_grid/components/users_list_page.test.tsx b/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.test.tsx
similarity index 63%
rename from x-pack/legacy/plugins/security/public/views/management/users_grid/components/users_list_page.test.tsx
rename to x-pack/plugins/security/public/management/users/users_grid/users_grid_page.test.tsx
index bdc0df9bae67c..def0649953437 100644
--- a/x-pack/legacy/plugins/security/public/views/management/users_grid/components/users_list_page.test.tsx
+++ b/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.test.tsx
@@ -4,19 +4,18 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { UserAPIClient } from '../../../../lib/api';
-import { User } from '../../../../../common/model';
+import { User } from '../../../../common/model';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
-import { UsersListPage } from './users_list_page';
+import { UsersGridPage } from './users_grid_page';
import React from 'react';
import { ReactWrapper } from 'enzyme';
+import { userAPIClientMock } from '../index.mock';
+import { coreMock } from '../../../../../../../src/core/public/mocks';
-jest.mock('ui/kfetch');
-
-describe('UsersListPage', () => {
+describe('UsersGridPage', () => {
it('renders the list of users', async () => {
- const apiClient = new UserAPIClient();
- apiClient.getUsers = jest.fn().mockImplementation(() => {
+ const apiClientMock = userAPIClientMock.create();
+ apiClientMock.getUsers.mockImplementation(() => {
return Promise.resolve([
{
username: 'foo',
@@ -38,22 +37,27 @@ describe('UsersListPage', () => {
]);
});
- const wrapper = mountWithIntl();
+ const wrapper = mountWithIntl(
+
+ );
await waitForRender(wrapper);
- expect(apiClient.getUsers).toBeCalledTimes(1);
+ expect(apiClientMock.getUsers).toBeCalledTimes(1);
expect(wrapper.find('EuiInMemoryTable')).toHaveLength(1);
expect(wrapper.find('EuiTableRow')).toHaveLength(2);
});
it('renders a forbidden message if user is not authorized', async () => {
- const apiClient = new UserAPIClient();
- apiClient.getUsers = jest.fn().mockImplementation(() => {
- return Promise.reject({ body: { statusCode: 403 } });
- });
+ const apiClient = userAPIClientMock.create();
+ apiClient.getUsers.mockRejectedValue({ body: { statusCode: 403 } });
- const wrapper = mountWithIntl();
+ const wrapper = mountWithIntl(
+
+ );
await waitForRender(wrapper);
diff --git a/x-pack/legacy/plugins/security/public/views/management/users_grid/components/users_list_page.tsx b/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.tsx
similarity index 85%
rename from x-pack/legacy/plugins/security/public/views/management/users_grid/components/users_list_page.tsx
rename to x-pack/plugins/security/public/management/users/users_grid/users_grid_page.tsx
index df8522e5f32f9..fa15c3388fcc9 100644
--- a/x-pack/legacy/plugins/security/public/views/management/users_grid/components/users_list_page.tsx
+++ b/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.tsx
@@ -19,15 +19,16 @@ import {
EuiEmptyPrompt,
EuiBasicTableColumn,
} from '@elastic/eui';
-import { toastNotifications } from 'ui/notify';
-import { injectI18n, FormattedMessage, InjectedIntl } from '@kbn/i18n/react';
-import { ConfirmDeleteUsers } from '../../../../components/management/users';
-import { User } from '../../../../../common/model';
-import { UserAPIClient } from '../../../../lib/api';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { NotificationsStart } from 'src/core/public';
+import { User } from '../../../../common/model';
+import { ConfirmDeleteUsers } from '../components';
+import { UserAPIClient } from '..';
interface Props {
- intl: InjectedIntl;
- apiClient: UserAPIClient;
+ apiClient: PublicMethodsOf;
+ notifications: NotificationsStart;
}
interface State {
@@ -38,7 +39,7 @@ interface State {
filter: string;
}
-class UsersListPageUI extends Component {
+export class UsersGridPage extends Component {
constructor(props: Props) {
super(props);
this.state = {
@@ -56,7 +57,6 @@ class UsersListPageUI extends Component {
public render() {
const { users, filter, permissionDenied, showDeleteConfirmation, selection } = this.state;
- const { intl } = this.props;
if (permissionDenied) {
return (
@@ -88,8 +88,7 @@ class UsersListPageUI extends Component {
const columns: Array> = [
{
field: 'full_name',
- name: intl.formatMessage({
- id: 'xpack.security.management.users.fullNameColumnName',
+ name: i18n.translate('xpack.security.management.users.fullNameColumnName', {
defaultMessage: 'Full Name',
}),
sortable: true,
@@ -100,8 +99,7 @@ class UsersListPageUI extends Component {
},
{
field: 'username',
- name: intl.formatMessage({
- id: 'xpack.security.management.users.userNameColumnName',
+ name: i18n.translate('xpack.security.management.users.userNameColumnName', {
defaultMessage: 'User Name',
}),
sortable: true,
@@ -114,8 +112,7 @@ class UsersListPageUI extends Component {
},
{
field: 'email',
- name: intl.formatMessage({
- id: 'xpack.security.management.users.emailAddressColumnName',
+ name: i18n.translate('xpack.security.management.users.emailAddressColumnName', {
defaultMessage: 'Email Address',
}),
sortable: true,
@@ -126,8 +123,7 @@ class UsersListPageUI extends Component {
},
{
field: 'roles',
- name: intl.formatMessage({
- id: 'xpack.security.management.users.rolesColumnName',
+ name: i18n.translate('xpack.security.management.users.rolesColumnName', {
defaultMessage: 'Roles',
}),
render: (rolenames: string[]) => {
@@ -144,15 +140,13 @@ class UsersListPageUI extends Component {
},
{
field: 'metadata',
- name: intl.formatMessage({
- id: 'xpack.security.management.users.reservedColumnName',
+ name: i18n.translate('xpack.security.management.users.reservedColumnName', {
defaultMessage: 'Reserved',
}),
sortable: ({ metadata }: User) => Boolean(metadata && metadata._reserved),
width: '100px',
align: 'right',
- description: intl.formatMessage({
- id: 'xpack.security.management.users.reservedColumnDescription',
+ description: i18n.translate('xpack.security.management.users.reservedColumnDescription', {
defaultMessage:
'Reserved users are built-in and cannot be removed. Only the password can be changed.',
}),
@@ -233,6 +227,7 @@ class UsersListPageUI extends Component {
usersToDelete={selection.map(user => user.username)}
callback={this.handleDelete}
apiClient={this.props.apiClient}
+ notifications={this.props.notifications}
/>
) : null}
@@ -275,14 +270,11 @@ class UsersListPageUI extends Component {
if (e.body.statusCode === 403) {
this.setState({ permissionDenied: true });
} else {
- toastNotifications.addDanger(
- this.props.intl.formatMessage(
- {
- id: 'xpack.security.management.users.fetchingUsersErrorMessage',
- defaultMessage: 'Error fetching users: {message}',
- },
- { message: e.body.message }
- )
+ this.props.notifications.toasts.addDanger(
+ i18n.translate('xpack.security.management.users.fetchingUsersErrorMessage', {
+ defaultMessage: 'Error fetching users: {message}',
+ values: { message: e.body.message },
+ })
);
}
}
@@ -315,5 +307,3 @@ class UsersListPageUI extends Component {
this.setState({ showDeleteConfirmation: false });
};
}
-
-export const UsersListPage = injectI18n(UsersListPageUI);
diff --git a/x-pack/plugins/security/public/management/users/users_management_app.test.tsx b/x-pack/plugins/security/public/management/users/users_management_app.test.tsx
new file mode 100644
index 0000000000000..48ffcfc550a84
--- /dev/null
+++ b/x-pack/plugins/security/public/management/users/users_management_app.test.tsx
@@ -0,0 +1,131 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+jest.mock('./users_grid', () => ({
+ UsersGridPage: (props: any) => `Users Page: ${JSON.stringify(props)}`,
+}));
+
+jest.mock('./edit_user', () => ({
+ EditUserPage: (props: any) => `User Edit Page: ${JSON.stringify(props)}`,
+}));
+
+import { usersManagementApp } from './users_management_app';
+
+import { coreMock } from '../../../../../../src/core/public/mocks';
+import { securityMock } from '../../mocks';
+
+async function mountApp(basePath: string) {
+ const container = document.createElement('div');
+ const setBreadcrumbs = jest.fn();
+
+ const unmount = await usersManagementApp
+ .create({
+ authc: securityMock.createSetup().authc,
+ getStartServices: coreMock.createSetup().getStartServices as any,
+ })
+ .mount({ basePath, element: container, setBreadcrumbs });
+
+ return { unmount, container, setBreadcrumbs };
+}
+
+describe('usersManagementApp', () => {
+ it('create() returns proper management app descriptor', () => {
+ expect(
+ usersManagementApp.create({
+ authc: securityMock.createSetup().authc,
+ getStartServices: coreMock.createSetup().getStartServices as any,
+ })
+ ).toMatchInlineSnapshot(`
+ Object {
+ "id": "users",
+ "mount": [Function],
+ "order": 10,
+ "title": "Users",
+ }
+ `);
+ });
+
+ it('mount() works for the `grid` page', async () => {
+ const basePath = '/some-base-path/users';
+ window.location.hash = basePath;
+
+ const { setBreadcrumbs, container, unmount } = await mountApp(basePath);
+
+ expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
+ expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `#${basePath}`, text: 'Users' }]);
+ expect(container).toMatchInlineSnapshot(`
+
+ Users Page: {"notifications":{"toasts":{}},"apiClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}}}
+
+ `);
+
+ unmount();
+
+ expect(container).toMatchInlineSnapshot(``);
+ });
+
+ it('mount() works for the `create user` page', async () => {
+ const basePath = '/some-base-path/users';
+ window.location.hash = `${basePath}/edit`;
+
+ const { setBreadcrumbs, container, unmount } = await mountApp(basePath);
+
+ expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
+ expect(setBreadcrumbs).toHaveBeenCalledWith([
+ { href: `#${basePath}`, text: 'Users' },
+ { text: 'Create' },
+ ]);
+ expect(container).toMatchInlineSnapshot(`
+
+ User Edit Page: {"authc":{},"apiClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"notifications":{"toasts":{}}}
+
+ `);
+
+ unmount();
+
+ expect(container).toMatchInlineSnapshot(``);
+ });
+
+ it('mount() works for the `edit user` page', async () => {
+ const basePath = '/some-base-path/users';
+ const userName = 'someUserName';
+ window.location.hash = `${basePath}/edit/${userName}`;
+
+ const { setBreadcrumbs, container, unmount } = await mountApp(basePath);
+
+ expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
+ expect(setBreadcrumbs).toHaveBeenCalledWith([
+ { href: `#${basePath}`, text: 'Users' },
+ { href: `#/some-base-path/users/edit/${userName}`, text: userName },
+ ]);
+ expect(container).toMatchInlineSnapshot(`
+
+ User Edit Page: {"authc":{},"apiClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"notifications":{"toasts":{}},"username":"someUserName"}
+
+ `);
+
+ unmount();
+
+ expect(container).toMatchInlineSnapshot(``);
+ });
+
+ it('mount() properly encodes user name in `edit user` page link in breadcrumbs', async () => {
+ const basePath = '/some-base-path/users';
+ const username = 'some 安全性 user';
+ window.location.hash = `${basePath}/edit/${username}`;
+
+ const { setBreadcrumbs } = await mountApp(basePath);
+
+ expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
+ expect(setBreadcrumbs).toHaveBeenCalledWith([
+ { href: `#${basePath}`, text: 'Users' },
+ {
+ href: '#/some-base-path/users/edit/some%20%E5%AE%89%E5%85%A8%E6%80%A7%20user',
+ text: username,
+ },
+ ]);
+ });
+});
diff --git a/x-pack/plugins/security/public/management/users/users_management_app.tsx b/x-pack/plugins/security/public/management/users/users_management_app.tsx
new file mode 100644
index 0000000000000..9aebb396ce9a9
--- /dev/null
+++ b/x-pack/plugins/security/public/management/users/users_management_app.tsx
@@ -0,0 +1,94 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { render, unmountComponentAtNode } from 'react-dom';
+import { HashRouter as Router, Route, Switch, useParams } from 'react-router-dom';
+import { i18n } from '@kbn/i18n';
+import { CoreSetup } from 'src/core/public';
+import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public';
+import { AuthenticationServiceSetup } from '../../authentication';
+import { PluginStartDependencies } from '../../plugin';
+import { RolesAPIClient } from '../roles';
+import { UserAPIClient } from './user_api_client';
+import { UsersGridPage } from './users_grid';
+import { EditUserPage } from './edit_user';
+
+interface CreateParams {
+ authc: AuthenticationServiceSetup;
+ getStartServices: CoreSetup['getStartServices'];
+}
+
+export const usersManagementApp = Object.freeze({
+ id: 'users',
+ create({ authc, getStartServices }: CreateParams) {
+ return {
+ id: this.id,
+ order: 10,
+ title: i18n.translate('xpack.security.management.usersTitle', { defaultMessage: 'Users' }),
+ async mount({ basePath, element, setBreadcrumbs }) {
+ const [{ http, notifications, i18n: i18nStart }] = await getStartServices();
+ const usersBreadcrumbs = [
+ {
+ text: i18n.translate('xpack.security.users.breadcrumb', { defaultMessage: 'Users' }),
+ href: `#${basePath}`,
+ },
+ ];
+
+ const userAPIClient = new UserAPIClient(http);
+ const UsersGridPageWithBreadcrumbs = () => {
+ setBreadcrumbs(usersBreadcrumbs);
+ return ;
+ };
+
+ const EditUserPageWithBreadcrumbs = () => {
+ const { username } = useParams<{ username?: string }>();
+
+ setBreadcrumbs([
+ ...usersBreadcrumbs,
+ username
+ ? { text: username, href: `#${basePath}/edit/${encodeURIComponent(username)}` }
+ : {
+ text: i18n.translate('xpack.security.users.createBreadcrumb', {
+ defaultMessage: 'Create',
+ }),
+ },
+ ]);
+
+ return (
+
+ );
+ };
+
+ render(
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ element
+ );
+
+ return () => {
+ unmountComponentAtNode(element);
+ };
+ },
+ } as RegisterManagementAppArgs;
+ },
+});
diff --git a/x-pack/plugins/security/public/plugin.ts b/x-pack/plugins/security/public/plugin.ts
deleted file mode 100644
index 50e0b838c750f..0000000000000
--- a/x-pack/plugins/security/public/plugin.ts
+++ /dev/null
@@ -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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { Plugin, CoreSetup, CoreStart } from 'src/core/public';
-import { LicensingPluginSetup } from '../../licensing/public';
-import {
- SessionExpired,
- SessionTimeout,
- ISessionTimeout,
- SessionTimeoutHttpInterceptor,
- UnauthorizedResponseHttpInterceptor,
-} from './session';
-import { SecurityLicenseService } from '../common/licensing';
-import { SecurityNavControlService } from './nav_control';
-import { AuthenticationService } from './authentication';
-
-export interface PluginSetupDependencies {
- licensing: LicensingPluginSetup;
-}
-
-export class SecurityPlugin implements Plugin {
- private sessionTimeout!: ISessionTimeout;
-
- private navControlService!: SecurityNavControlService;
-
- private securityLicenseService!: SecurityLicenseService;
-
- public setup(core: CoreSetup, { licensing }: PluginSetupDependencies) {
- const { http, notifications, injectedMetadata } = core;
- const { basePath, anonymousPaths } = http;
- anonymousPaths.register('/login');
- anonymousPaths.register('/logout');
- anonymousPaths.register('/logged_out');
-
- const tenant = `${injectedMetadata.getInjectedVar('session.tenant', '')}`;
- const sessionExpired = new SessionExpired(basePath, tenant);
- http.intercept(new UnauthorizedResponseHttpInterceptor(sessionExpired, anonymousPaths));
- this.sessionTimeout = new SessionTimeout(notifications, sessionExpired, http, tenant);
- http.intercept(new SessionTimeoutHttpInterceptor(this.sessionTimeout, anonymousPaths));
-
- this.navControlService = new SecurityNavControlService();
- this.securityLicenseService = new SecurityLicenseService();
- const { license } = this.securityLicenseService.setup({ license$: licensing.license$ });
-
- const authc = new AuthenticationService().setup({ http: core.http });
-
- this.navControlService.setup({
- securityLicense: license,
- authc,
- });
-
- return {
- authc,
- sessionTimeout: this.sessionTimeout,
- };
- }
-
- public start(core: CoreStart) {
- this.sessionTimeout.start();
- this.navControlService.start({ core });
- }
-
- public stop() {
- this.sessionTimeout.stop();
- this.navControlService.stop();
- this.securityLicenseService.stop();
- }
-}
-
-export type SecurityPluginSetup = ReturnType;
-export type SecurityPluginStart = ReturnType;
diff --git a/x-pack/plugins/security/public/plugin.tsx b/x-pack/plugins/security/public/plugin.tsx
new file mode 100644
index 0000000000000..394e23cbbf646
--- /dev/null
+++ b/x-pack/plugins/security/public/plugin.tsx
@@ -0,0 +1,147 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { Plugin, CoreSetup, CoreStart } from 'src/core/public';
+import { i18n } from '@kbn/i18n';
+import React from 'react';
+import { DataPublicPluginStart } from '../../../../src/plugins/data/public';
+import {
+ FeatureCatalogueCategory,
+ HomePublicPluginSetup,
+} from '../../../../src/plugins/home/public';
+import { LicensingPluginSetup } from '../../licensing/public';
+import { ManagementSetup, ManagementStart } from '../../../../src/plugins/management/public';
+import {
+ SessionExpired,
+ SessionTimeout,
+ ISessionTimeout,
+ SessionTimeoutHttpInterceptor,
+ UnauthorizedResponseHttpInterceptor,
+} from './session';
+import { SecurityLicenseService } from '../common/licensing';
+import { SecurityNavControlService } from './nav_control';
+import { AccountManagementPage } from './account_management';
+import { AuthenticationService, AuthenticationServiceSetup } from './authentication';
+import { ManagementService, UserAPIClient } from './management';
+
+export interface PluginSetupDependencies {
+ licensing: LicensingPluginSetup;
+ home?: HomePublicPluginSetup;
+ management?: ManagementSetup;
+}
+
+export interface PluginStartDependencies {
+ data: DataPublicPluginStart;
+ management?: ManagementStart;
+}
+
+export class SecurityPlugin
+ implements
+ Plugin<
+ SecurityPluginSetup,
+ SecurityPluginStart,
+ PluginSetupDependencies,
+ PluginStartDependencies
+ > {
+ private sessionTimeout!: ISessionTimeout;
+ private readonly navControlService = new SecurityNavControlService();
+ private readonly securityLicenseService = new SecurityLicenseService();
+ private readonly managementService = new ManagementService();
+ private authc!: AuthenticationServiceSetup;
+
+ public setup(
+ core: CoreSetup,
+ { home, licensing, management }: PluginSetupDependencies
+ ) {
+ const { http, notifications, injectedMetadata } = core;
+ const { basePath, anonymousPaths } = http;
+ anonymousPaths.register('/login');
+ anonymousPaths.register('/logout');
+ anonymousPaths.register('/logged_out');
+
+ const tenant = `${injectedMetadata.getInjectedVar('session.tenant', '')}`;
+ const sessionExpired = new SessionExpired(basePath, tenant);
+ http.intercept(new UnauthorizedResponseHttpInterceptor(sessionExpired, anonymousPaths));
+ this.sessionTimeout = new SessionTimeout(notifications, sessionExpired, http, tenant);
+ http.intercept(new SessionTimeoutHttpInterceptor(this.sessionTimeout, anonymousPaths));
+
+ const { license } = this.securityLicenseService.setup({ license$: licensing.license$ });
+
+ this.authc = new AuthenticationService().setup({ http: core.http });
+
+ this.navControlService.setup({
+ securityLicense: license,
+ authc: this.authc,
+ });
+
+ if (management) {
+ this.managementService.setup({
+ license,
+ management,
+ authc: this.authc,
+ fatalErrors: core.fatalErrors,
+ getStartServices: core.getStartServices,
+ });
+ }
+
+ if (management && home) {
+ home.featureCatalogue.register({
+ id: 'security',
+ title: i18n.translate('xpack.security.registerFeature.securitySettingsTitle', {
+ defaultMessage: 'Security Settings',
+ }),
+ description: i18n.translate('xpack.security.registerFeature.securitySettingsDescription', {
+ defaultMessage:
+ 'Protect your data and easily manage who has access to what with users and roles.',
+ }),
+ icon: 'securityApp',
+ path: '/app/kibana#/management/security/users',
+ showOnHomePage: true,
+ category: FeatureCatalogueCategory.ADMIN,
+ });
+ }
+
+ return {
+ authc: this.authc,
+ sessionTimeout: this.sessionTimeout,
+ };
+ }
+
+ public start(core: CoreStart, { data, management }: PluginStartDependencies) {
+ this.sessionTimeout.start();
+ this.navControlService.start({ core });
+
+ if (management) {
+ this.managementService.start({ management });
+ }
+
+ return {
+ __legacyCompat: {
+ account_management: {
+ AccountManagementPage: () => (
+
+
+
+ ),
+ },
+ },
+ };
+ }
+
+ public stop() {
+ this.sessionTimeout.stop();
+ this.navControlService.stop();
+ this.securityLicenseService.stop();
+ this.managementService.stop();
+ }
+}
+
+export type SecurityPluginSetup = ReturnType;
+export type SecurityPluginStart = ReturnType;
diff --git a/x-pack/plugins/security/server/routes/role_mapping/get.ts b/x-pack/plugins/security/server/routes/role_mapping/get.ts
index 9cd5cf83092e1..def6fabc0e322 100644
--- a/x-pack/plugins/security/server/routes/role_mapping/get.ts
+++ b/x-pack/plugins/security/server/routes/role_mapping/get.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { schema } from '@kbn/config-schema';
-import { RoleMapping } from '../../../../../legacy/plugins/security/common/model';
+import { RoleMapping } from '../../../common/model';
import { createLicensedRouteHandler } from '../licensed_route_handler';
import { wrapError } from '../../errors';
import { RouteDefinitionParams } from '..';
diff --git a/x-pack/plugins/spaces/common/index.ts b/x-pack/plugins/spaces/common/index.ts
index 65baa1bd99102..c1f0f8bd3ece4 100644
--- a/x-pack/plugins/spaces/common/index.ts
+++ b/x-pack/plugins/spaces/common/index.ts
@@ -5,5 +5,5 @@
*/
export { isReservedSpace } from './is_reserved_space';
-export { MAX_SPACE_INITIALS } from './constants';
+export { MAX_SPACE_INITIALS, SPACE_SEARCH_COUNT_THRESHOLD } from './constants';
export { addSpaceIdToPath, getSpaceIdFromPath } from './lib/spaces_url_parser';
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index ce3edbbb59828..c3e46a9c04e1c 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -10595,17 +10595,6 @@
"xpack.security.management.apiKeys.table.userFilterLabel": "ユーザー",
"xpack.security.management.apiKeys.table.userNameColumnName": "ユーザー",
"xpack.security.management.apiKeysTitle": "API キー",
- "xpack.security.management.changePasswordForm.cancelButtonLabel": "キャンセル",
- "xpack.security.management.changePasswordForm.changePasswordLinkLabel": "パスワードを変更",
- "xpack.security.management.changePasswordForm.confirmPasswordLabel": "パスワードの確認",
- "xpack.security.management.changePasswordForm.currentPasswordLabel": "現在のパスワード",
- "xpack.security.management.changePasswordForm.incorrectPasswordDescription": "入力された現在のパスワードが正しくありません。",
- "xpack.security.management.changePasswordForm.newPasswordLabel": "新しいパスワード",
- "xpack.security.management.changePasswordForm.passwordDontMatchDescription": "パスワードが一致しません",
- "xpack.security.management.changePasswordForm.passwordLabel": "パスワード",
- "xpack.security.management.changePasswordForm.passwordLengthDescription": "パスワードは最低 6 文字必要です",
- "xpack.security.management.changePasswordForm.saveChangesButtonLabel": "変更を保存",
- "xpack.security.management.changePasswordForm.updateAndRestartKibanaDescription": "Kibana ユーザーのパスワードを変更後、kibana.yml ファイルを更新し Kibana を再起動する必要があります。",
"xpack.security.management.editRole.cancelButtonLabel": "キャンセル",
"xpack.security.management.editRole.changeAllPrivilegesLink": "(すべて変更)",
"xpack.security.management.editRole.collapsiblePanel.hideLinkText": "非表示",
@@ -10730,10 +10719,6 @@
"xpack.security.management.editRolespacePrivilegeForm.createPrivilegeButton": "スペース権限を作成",
"xpack.security.management.editRolespacePrivilegeForm.updateGlobalPrivilegeButton": "グローバル特権を更新",
"xpack.security.management.editRolespacePrivilegeForm.updatePrivilegeButton": "スペース権限を更新",
- "xpack.security.management.passwordForm.confirmPasswordLabel": "パスワードの確認",
- "xpack.security.management.passwordForm.passwordDontMatchDescription": "パスワードが一致しません",
- "xpack.security.management.passwordForm.passwordLabel": "パスワード",
- "xpack.security.management.passwordForm.passwordLengthDescription": "パスワードは最低 6 文字必要です",
"xpack.security.management.roles.actionsColumnName": "アクション",
"xpack.security.management.roles.cloneRoleActionName": "{roleName} を複製",
"xpack.security.management.roles.confirmDelete.cancelButtonLabel": "キャンセル",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index dc72e83ebe3c3..c40b8c8d0393c 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -10594,17 +10594,6 @@
"xpack.security.management.apiKeys.table.userFilterLabel": "用户",
"xpack.security.management.apiKeys.table.userNameColumnName": "用户",
"xpack.security.management.apiKeysTitle": "API 密钥",
- "xpack.security.management.changePasswordForm.cancelButtonLabel": "取消",
- "xpack.security.management.changePasswordForm.changePasswordLinkLabel": "更改密码",
- "xpack.security.management.changePasswordForm.confirmPasswordLabel": "确认密码",
- "xpack.security.management.changePasswordForm.currentPasswordLabel": "当前密码",
- "xpack.security.management.changePasswordForm.incorrectPasswordDescription": "您输入的当前密码不正确。",
- "xpack.security.management.changePasswordForm.newPasswordLabel": "新密码",
- "xpack.security.management.changePasswordForm.passwordDontMatchDescription": "密码不匹配",
- "xpack.security.management.changePasswordForm.passwordLabel": "密码",
- "xpack.security.management.changePasswordForm.passwordLengthDescription": "密码长度必须至少为 6 个字符",
- "xpack.security.management.changePasswordForm.saveChangesButtonLabel": "保存更改",
- "xpack.security.management.changePasswordForm.updateAndRestartKibanaDescription": "更改 Kibana 用户的密码后,必须更新 kibana.yml 文件并重新启动 Kibana",
"xpack.security.management.editRole.cancelButtonLabel": "取消",
"xpack.security.management.editRole.changeAllPrivilegesLink": "(全部更改)",
"xpack.security.management.editRole.collapsiblePanel.hideLinkText": "隐藏",
@@ -10729,10 +10718,6 @@
"xpack.security.management.editRolespacePrivilegeForm.createPrivilegeButton": "创建工作区权限",
"xpack.security.management.editRolespacePrivilegeForm.updateGlobalPrivilegeButton": "更新全局权限",
"xpack.security.management.editRolespacePrivilegeForm.updatePrivilegeButton": "更新工作区权限",
- "xpack.security.management.passwordForm.confirmPasswordLabel": "确认密码",
- "xpack.security.management.passwordForm.passwordDontMatchDescription": "密码不匹配",
- "xpack.security.management.passwordForm.passwordLabel": "密码",
- "xpack.security.management.passwordForm.passwordLengthDescription": "密码长度必须至少为 6 个字符",
"xpack.security.management.roles.actionsColumnName": "鎿嶄綔",
"xpack.security.management.roles.cloneRoleActionName": "克隆 {roleName}",
"xpack.security.management.roles.confirmDelete.cancelButtonLabel": "取消",
diff --git a/x-pack/test/functional/apps/security/management.js b/x-pack/test/functional/apps/security/management.js
index 45a35029ffba2..8ab84126b2b30 100644
--- a/x-pack/test/functional/apps/security/management.js
+++ b/x-pack/test/functional/apps/security/management.js
@@ -11,7 +11,7 @@ import {
ROLES_PATH,
EDIT_ROLES_PATH,
CLONE_ROLES_PATH,
-} from '../../../../legacy/plugins/security/public/views/management/management_urls';
+} from '../../../../plugins/security/public/management/management_urls';
export default function({ getService, getPageObjects }) {
const kibanaServer = getService('kibanaServer');
diff --git a/x-pack/test/functional/apps/security/role_mappings.ts b/x-pack/test/functional/apps/security/role_mappings.ts
index 5fed56ee79e3d..a1517e1934a28 100644
--- a/x-pack/test/functional/apps/security/role_mappings.ts
+++ b/x-pack/test/functional/apps/security/role_mappings.ts
@@ -93,7 +93,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
const url = parse(await browser.getCurrentUrl());
- expect(url.hash).to.eql('#/management/security/role_mappings?_g=()');
+ expect(url.hash).to.eql('#/management/security/role_mappings');
});
describe('with role mappings', () => {
From a131f1dbcfa4838653d587e948ef0365036e5dd6 Mon Sep 17 00:00:00 2001
From: Dima Arnautov
Date: Tue, 21 Jan 2020 13:50:18 +0100
Subject: [PATCH 004/187] [ML] Formatting for additional timing and model size
stats (#55062)
* [ML] formatting for additional timing and model size stats
* [ML] roundToDecimalPlace only average search time
* [ML] adjust functional tests
* [ML] remove debug tag, fix assert value
* [ML] check for no decimal place
* [ML] fix functional tests
Co-authored-by: Elastic Machine
---
.../formatters/round_to_decimal_place.test.ts | 2 ++
.../formatters/round_to_decimal_place.ts | 7 ++++++-
.../components/job_details/format_values.js | 10 ++++++++++
.../anomaly_detection/advanced_job.ts | 8 ++++----
.../anomaly_detection/multi_metric_job.ts | 4 ++--
.../anomaly_detection/population_job.ts | 4 ++--
.../anomaly_detection/saved_search_job.ts | 20 +++++++++----------
.../anomaly_detection/single_metric_job.ts | 4 ++--
8 files changed, 38 insertions(+), 21 deletions(-)
diff --git a/x-pack/legacy/plugins/ml/public/application/formatters/round_to_decimal_place.test.ts b/x-pack/legacy/plugins/ml/public/application/formatters/round_to_decimal_place.test.ts
index 99b9aceab3696..663a3f3d8f955 100644
--- a/x-pack/legacy/plugins/ml/public/application/formatters/round_to_decimal_place.test.ts
+++ b/x-pack/legacy/plugins/ml/public/application/formatters/round_to_decimal_place.test.ts
@@ -20,6 +20,7 @@ describe('ML - roundToDecimalPlace formatter', () => {
expect(roundToDecimalPlace(0.0005)).toBe('5.00e-4');
expect(roundToDecimalPlace(-0.0005)).toBe('-5.00e-4');
expect(roundToDecimalPlace(-12.045)).toBe(-12.04);
+ expect(roundToDecimalPlace(0)).toBe(0);
});
it('returns the correct format using specified decimal place', () => {
@@ -31,5 +32,6 @@ describe('ML - roundToDecimalPlace formatter', () => {
expect(roundToDecimalPlace(0.0005, 4)).toBe(0.0005);
expect(roundToDecimalPlace(0.00005, 4)).toBe('5.00e-5');
expect(roundToDecimalPlace(-0.00005, 4)).toBe('-5.00e-5');
+ expect(roundToDecimalPlace(0, 4)).toBe(0);
});
});
diff --git a/x-pack/legacy/plugins/ml/public/application/formatters/round_to_decimal_place.ts b/x-pack/legacy/plugins/ml/public/application/formatters/round_to_decimal_place.ts
index f863fe6d76e57..5a030d7619e98 100644
--- a/x-pack/legacy/plugins/ml/public/application/formatters/round_to_decimal_place.ts
+++ b/x-pack/legacy/plugins/ml/public/application/formatters/round_to_decimal_place.ts
@@ -4,7 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export function roundToDecimalPlace(num: number, dp: number = 2) {
+export function roundToDecimalPlace(num: number, dp: number = 2): number | string {
+ if (num % 1 === 0) {
+ // no decimal place
+ return num;
+ }
+
if (Math.abs(num) < Math.pow(10, -dp)) {
return Number.parseFloat(String(num)).toExponential(2);
}
diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/jobs_list/components/job_details/format_values.js b/x-pack/legacy/plugins/ml/public/application/jobs/jobs_list/components/job_details/format_values.js
index eb0a905725d75..9984f3be299ae 100644
--- a/x-pack/legacy/plugins/ml/public/application/jobs/jobs_list/components/job_details/format_values.js
+++ b/x-pack/legacy/plugins/ml/public/application/jobs/jobs_list/components/job_details/format_values.js
@@ -6,6 +6,7 @@
import numeral from '@elastic/numeral';
import { formatDate } from '@elastic/eui/lib/services/format';
+import { roundToDecimalPlace } from '../../../../formatters/round_to_decimal_place';
import { toLocaleString } from '../../../../util/string_utils';
const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
@@ -35,6 +36,8 @@ export function formatValues([key, value]) {
case 'established_model_memory':
case 'input_bytes':
case 'model_bytes':
+ case 'model_bytes_exceeded':
+ case 'model_bytes_memory_limit':
value = formatData(value);
break;
@@ -53,9 +56,16 @@ export function formatValues([key, value]) {
case 'total_over_field_count':
case 'total_partition_field_count':
case 'bucket_allocation_failures_count':
+ case 'search_count':
value = toLocaleString(value);
break;
+ // numbers rounded to 3 decimal places
+ case 'average_search_time_per_bucket_ms':
+ case 'exponential_average_search_time_per_hour_ms':
+ value = typeof value === 'number' ? roundToDecimalPlace(value, 3).toLocaleString() : value;
+ break;
+
default:
break;
}
diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/advanced_job.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/advanced_job.ts
index 386914b735554..acb92b270c4a1 100644
--- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/advanced_job.ts
+++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/advanced_job.ts
@@ -194,8 +194,8 @@ export default function({ getService }: FtrProviderContext) {
},
modelSizeStats: {
result_type: 'model_size_stats',
- model_bytes_exceeded: '0',
- model_bytes_memory_limit: '10485760',
+ model_bytes_exceeded: '0.0 B',
+ model_bytes_memory_limit: '10.0 MB',
total_by_field_count: '37',
total_over_field_count: '92',
total_partition_field_count: '8',
@@ -261,8 +261,8 @@ export default function({ getService }: FtrProviderContext) {
},
modelSizeStats: {
result_type: 'model_size_stats',
- model_bytes_exceeded: '0',
- model_bytes_memory_limit: '104857600',
+ model_bytes_exceeded: '0.0 B',
+ model_bytes_memory_limit: '100.0 MB',
total_by_field_count: '994',
total_over_field_count: '0',
total_partition_field_count: '2',
diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/multi_metric_job.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/multi_metric_job.ts
index d41d96e40e2be..6a12a28e8ac49 100644
--- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/multi_metric_job.ts
+++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/multi_metric_job.ts
@@ -60,8 +60,8 @@ export default function({ getService }: FtrProviderContext) {
return {
job_id: expectedJobId,
result_type: 'model_size_stats',
- model_bytes_exceeded: '0',
- model_bytes_memory_limit: '20971520',
+ model_bytes_exceeded: '0.0 B',
+ model_bytes_memory_limit: '20.0 MB',
total_by_field_count: '59',
total_over_field_count: '0',
total_partition_field_count: '58',
diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/population_job.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/population_job.ts
index 296af3179ce3e..6593dd10928b4 100644
--- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/population_job.ts
+++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/population_job.ts
@@ -74,8 +74,8 @@ export default function({ getService }: FtrProviderContext) {
return {
job_id: expectedJobId,
result_type: 'model_size_stats',
- model_bytes_exceeded: '0',
- model_bytes_memory_limit: '8388608',
+ model_bytes_exceeded: '0.0 B',
+ model_bytes_memory_limit: '8.0 MB',
total_by_field_count: '25',
total_over_field_count: '92',
total_partition_field_count: '3',
diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/saved_search_job.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/saved_search_job.ts
index 7d989bc6244b8..348910a2a8f84 100644
--- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/saved_search_job.ts
+++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/saved_search_job.ts
@@ -53,8 +53,8 @@ export default function({ getService }: FtrProviderContext) {
},
modelSizeStats: {
result_type: 'model_size_stats',
- model_bytes_exceeded: '0',
- model_bytes_memory_limit: '20971520',
+ model_bytes_exceeded: '0.0 B',
+ model_bytes_memory_limit: '20.0 MB',
total_by_field_count: '3',
total_over_field_count: '0',
total_partition_field_count: '2',
@@ -104,8 +104,8 @@ export default function({ getService }: FtrProviderContext) {
},
modelSizeStats: {
result_type: 'model_size_stats',
- model_bytes_exceeded: '0',
- model_bytes_memory_limit: '20971520',
+ model_bytes_exceeded: '0.0 B',
+ model_bytes_memory_limit: '20.0 MB',
total_by_field_count: '7',
total_over_field_count: '0',
total_partition_field_count: '6',
@@ -155,8 +155,8 @@ export default function({ getService }: FtrProviderContext) {
},
modelSizeStats: {
result_type: 'model_size_stats',
- model_bytes_exceeded: '0',
- model_bytes_memory_limit: '20971520',
+ model_bytes_exceeded: '0.0 B',
+ model_bytes_memory_limit: '20.0 MB',
total_by_field_count: '7',
total_over_field_count: '0',
total_partition_field_count: '6',
@@ -207,8 +207,8 @@ export default function({ getService }: FtrProviderContext) {
},
modelSizeStats: {
result_type: 'model_size_stats',
- model_bytes_exceeded: '0',
- model_bytes_memory_limit: '20971520',
+ model_bytes_exceeded: '0.0 B',
+ model_bytes_memory_limit: '20.0 MB',
total_by_field_count: '3',
total_over_field_count: '0',
total_partition_field_count: '2',
@@ -258,8 +258,8 @@ export default function({ getService }: FtrProviderContext) {
},
modelSizeStats: {
result_type: 'model_size_stats',
- model_bytes_exceeded: '0',
- model_bytes_memory_limit: '20971520',
+ model_bytes_exceeded: '0.0 B',
+ model_bytes_memory_limit: '20.0 MB',
total_by_field_count: '3',
total_over_field_count: '0',
total_partition_field_count: '2',
diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_job.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_job.ts
index f6cd7b40bc7b1..13cac36d99a1b 100644
--- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_job.ts
+++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_job.ts
@@ -59,8 +59,8 @@ export default function({ getService }: FtrProviderContext) {
return {
job_id: expectedJobId,
result_type: 'model_size_stats',
- model_bytes_exceeded: '0',
- model_bytes_memory_limit: '15728640',
+ model_bytes_exceeded: '0.0 B',
+ model_bytes_memory_limit: '15.0 MB',
total_by_field_count: '3',
total_over_field_count: '0',
total_partition_field_count: '2',
From c1960583500d28fc3002b4959804ad52cf723b4a Mon Sep 17 00:00:00 2001
From: patrykkopycinski
Date: Tue, 21 Jan 2020 14:04:52 +0100
Subject: [PATCH 005/187] [SIEM] Update ml_conditional_links cypress tests
(#55373)
---
.../ml_conditional_links/ml_conditional_links.spec.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts
index afeb8c3c13a4f..142729189e49b 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts
@@ -99,7 +99,7 @@ describe('ml conditional links', () => {
loginAndWaitForPage(mlNetworkSingleIpNullKqlQuery);
cy.url().should(
'include',
- '/app/siem#/network/ip/127.0.0.1?timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))'
+ '/app/siem#/network/ip/127.0.0.1/source?timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))'
);
});
@@ -107,7 +107,7 @@ describe('ml conditional links', () => {
loginAndWaitForPage(mlNetworkSingleIpKqlQuery);
cy.url().should(
'include',
- "/app/siem#/network/ip/127.0.0.1?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))"
+ "/app/siem#/network/ip/127.0.0.1/source?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))"
);
});
From c88aa5a505ced73ffabd4a4136f6172ee23073c8 Mon Sep 17 00:00:00 2001
From: Joe Reuter
Date: Tue, 21 Jan 2020 14:49:30 +0100
Subject: [PATCH 006/187] Migration: Separate legacy and index entrypoint
(#54124)
---
src/legacy/core_plugins/kibana/index.js | 7 +-
.../core_plugins/kibana/public/.eslintrc.js | 1 +
.../kibana/public/dashboard/index.ts | 40 ++----------
.../kibana/public/dashboard/legacy.ts | 56 ++++++++++++++++
.../__tests__/directives/discover_field.js | 2 +-
.../__tests__/directives/field_calculator.js | 2 +-
.../__tests__/directives/field_chooser.js | 2 +-
.../discover/__tests__/doc_table/doc_table.js | 2 +-
.../__tests__/doc_table/lib/rows_headers.js | 2 +-
.../query_parameters/action_add_filter.js | 2 +-
.../action_set_predecessor_count.js | 2 +-
.../action_set_query_parameters.js | 2 +-
.../action_set_successor_count.js | 2 +-
.../kibana/public/discover/index.ts | 17 +----
.../kibana/public/discover/legacy.ts | 32 +++++++++
.../angular/context/api/__tests__/anchor.js | 2 +-
.../context/api/__tests__/predecessors.js | 2 +-
.../context/api/__tests__/successors.js | 2 +-
.../core_plugins/kibana/public/home/index.ts | 5 --
.../kibana/public/home/kibana_services.ts | 1 -
.../home/np_ready/components/welcome.test.tsx | 4 --
.../home/np_ready/components/welcome.tsx | 7 +-
.../core_plugins/kibana/public/home/plugin.ts | 8 ++-
.../core_plugins/kibana/public/kibana.js | 6 +-
.../kibana/public/visualize/index.ts | 49 ++------------
.../kibana/public/visualize/legacy.ts | 65 +++++++++++++++++++
.../directives/__tests__/css_truncate.js | 2 +-
.../dashboard_mode/public/dashboard_viewer.js | 1 +
28 files changed, 202 insertions(+), 123 deletions(-)
create mode 100644 src/legacy/core_plugins/kibana/public/dashboard/legacy.ts
create mode 100644 src/legacy/core_plugins/kibana/public/discover/legacy.ts
create mode 100644 src/legacy/core_plugins/kibana/public/visualize/legacy.ts
diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js
index e6a0420534ef2..0366d8b27f211 100644
--- a/src/legacy/core_plugins/kibana/index.js
+++ b/src/legacy/core_plugins/kibana/index.js
@@ -60,7 +60,12 @@ export default function(kibana) {
},
uiExports: {
- hacks: ['plugins/kibana/discover', 'plugins/kibana/dev_tools', 'plugins/kibana/visualize'],
+ hacks: [
+ 'plugins/kibana/discover/legacy',
+ 'plugins/kibana/dev_tools',
+ 'plugins/kibana/visualize/legacy',
+ 'plugins/kibana/dashboard/legacy',
+ ],
savedObjectTypes: ['plugins/kibana/dashboard/saved_dashboard/saved_dashboard_register'],
app: {
id: 'kibana',
diff --git a/src/legacy/core_plugins/kibana/public/.eslintrc.js b/src/legacy/core_plugins/kibana/public/.eslintrc.js
index 160adcc5b63f1..9b45217287dc8 100644
--- a/src/legacy/core_plugins/kibana/public/.eslintrc.js
+++ b/src/legacy/core_plugins/kibana/public/.eslintrc.js
@@ -51,6 +51,7 @@ function buildRestrictedPaths(shimmedPlugins) {
from: [
`src/legacy/core_plugins/kibana/public/${shimmedPlugin}/**/*`,
`!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/index.ts`,
+ `!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/legacy.ts`,
],
allowSameFolder: false,
errorMessage: `kibana/public/${shimmedPlugin} is behaving like a NP plugin and does not allow deep imports. If you need something from within ${shimmedPlugin} in another plugin, consider re-exporting it from the top level index module`,
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/index.ts b/src/legacy/core_plugins/kibana/public/dashboard/index.ts
index fd39f28a7673a..4a8decab6b00e 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/index.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/index.ts
@@ -17,40 +17,12 @@
* under the License.
*/
-import { npSetup, npStart, legacyChrome } from './legacy_imports';
-import { DashboardPlugin, LegacyAngularInjectedDependencies } from './plugin';
-import { start as data } from '../../../data/public/legacy';
-import { start as embeddables } from '../../../embeddable_api/public/np_ready/public/legacy';
-import './saved_dashboard/saved_dashboard_register';
-import './dashboard_config';
+import { PluginInitializerContext } from 'kibana/public';
+import { DashboardPlugin } from './plugin';
export * from './np_ready/dashboard_constants';
-/**
- * Get dependencies relying on the global angular context.
- * They also have to get resolved together with the legacy imports above
- */
-async function getAngularDependencies(): Promise {
- const injector = await legacyChrome.dangerouslyGetActiveInjector();
-
- return {
- dashboardConfig: injector.get('dashboardConfig'),
- };
-}
-
-(async () => {
- const instance = new DashboardPlugin();
- instance.setup(npSetup.core, {
- ...npSetup.plugins,
- __LEGACY: {
- getAngularDependencies,
- },
- });
- instance.start(npStart.core, {
- ...npStart.plugins,
- data,
- npData: npStart.plugins.data,
- embeddables,
- navigation: npStart.plugins.navigation,
- });
-})();
+// Core will be looking for this when loading our plugin in the new platform
+export const plugin = (context: PluginInitializerContext) => {
+ return new DashboardPlugin();
+};
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/legacy.ts b/src/legacy/core_plugins/kibana/public/dashboard/legacy.ts
new file mode 100644
index 0000000000000..068a8378f936a
--- /dev/null
+++ b/src/legacy/core_plugins/kibana/public/dashboard/legacy.ts
@@ -0,0 +1,56 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { PluginInitializerContext } from 'kibana/public';
+import { npSetup, npStart, legacyChrome } from './legacy_imports';
+import { LegacyAngularInjectedDependencies } from './plugin';
+import { start as data } from '../../../data/public/legacy';
+import { start as embeddables } from '../../../embeddable_api/public/np_ready/public/legacy';
+import './saved_dashboard/saved_dashboard_register';
+import './dashboard_config';
+import { plugin } from './index';
+
+/**
+ * Get dependencies relying on the global angular context.
+ * They also have to get resolved together with the legacy imports above
+ */
+async function getAngularDependencies(): Promise {
+ const injector = await legacyChrome.dangerouslyGetActiveInjector();
+
+ return {
+ dashboardConfig: injector.get('dashboardConfig'),
+ };
+}
+
+(async () => {
+ const instance = plugin({} as PluginInitializerContext);
+ instance.setup(npSetup.core, {
+ ...npSetup.plugins,
+ __LEGACY: {
+ getAngularDependencies,
+ },
+ });
+ instance.start(npStart.core, {
+ ...npStart.plugins,
+ data,
+ npData: npStart.plugins.data,
+ embeddables,
+ navigation: npStart.plugins.navigation,
+ });
+})();
diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/discover_field.js b/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/discover_field.js
index 18d83595f8fa3..6ffda87ac2be8 100644
--- a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/discover_field.js
+++ b/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/discover_field.js
@@ -22,7 +22,7 @@ import _ from 'lodash';
import sinon from 'sinon';
import ngMock from 'ng_mock';
import expect from '@kbn/expect';
-import { pluginInstance } from 'plugins/kibana/discover/index';
+import { pluginInstance } from 'plugins/kibana/discover/legacy';
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
// Load the kibana app dependencies.
diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_calculator.js b/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_calculator.js
index 378a9e9325655..f302d684135f6 100644
--- a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_calculator.js
+++ b/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_calculator.js
@@ -18,7 +18,7 @@
*/
import _ from 'lodash';
-import { pluginInstance } from 'plugins/kibana/discover/index';
+import { pluginInstance } from 'plugins/kibana/discover/legacy';
import ngMock from 'ng_mock';
import { fieldCalculator } from '../../np_ready/components/field_chooser/lib/field_calculator';
import expect from '@kbn/expect';
diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_chooser.js b/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_chooser.js
index 5f6898ae2bd16..f74e145865475 100644
--- a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_chooser.js
+++ b/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_chooser.js
@@ -23,7 +23,7 @@ import _ from 'lodash';
import sinon from 'sinon';
import expect from '@kbn/expect';
import $ from 'jquery';
-import { pluginInstance } from 'plugins/kibana/discover/index';
+import { pluginInstance } from 'plugins/kibana/discover/legacy';
import FixturesHitsProvider from 'fixtures/hits';
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
import { SimpleSavedObject } from '../../../../../../../core/public';
diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/doc_table.js b/src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/doc_table.js
index b57f452b637af..6b97da79fc589 100644
--- a/src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/doc_table.js
+++ b/src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/doc_table.js
@@ -22,7 +22,7 @@ import expect from '@kbn/expect';
import _ from 'lodash';
import ngMock from 'ng_mock';
import 'ui/private';
-import { pluginInstance } from 'plugins/kibana/discover/index';
+import { pluginInstance } from 'plugins/kibana/discover/legacy';
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
import hits from 'fixtures/real_hits';
diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/lib/rows_headers.js b/src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/lib/rows_headers.js
index 012f2b6061ee4..c19e033ccb72d 100644
--- a/src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/lib/rows_headers.js
+++ b/src/legacy/core_plugins/kibana/public/discover/__tests__/doc_table/lib/rows_headers.js
@@ -24,7 +24,7 @@ import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import { getFakeRow, getFakeRowVals } from 'fixtures/fake_row';
import $ from 'jquery';
-import { pluginInstance } from 'plugins/kibana/discover/index';
+import { pluginInstance } from 'plugins/kibana/discover/legacy';
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
describe('Doc Table', function() {
diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/query_parameters/action_add_filter.js b/src/legacy/core_plugins/kibana/public/discover/__tests__/query_parameters/action_add_filter.js
index 90614cf3c132c..f2acbf363d825 100644
--- a/src/legacy/core_plugins/kibana/public/discover/__tests__/query_parameters/action_add_filter.js
+++ b/src/legacy/core_plugins/kibana/public/discover/__tests__/query_parameters/action_add_filter.js
@@ -22,7 +22,7 @@ import ngMock from 'ng_mock';
import { createStateStub } from './_utils';
import { getQueryParameterActions } from '../../np_ready/angular/context/query_parameters/actions';
import { createIndexPatternsStub } from '../../np_ready/angular/context/api/__tests__/_stubs';
-import { pluginInstance } from 'plugins/kibana/discover/index';
+import { pluginInstance } from 'plugins/kibana/discover/legacy';
import { npStart } from 'ui/new_platform';
describe('context app', function() {
diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/query_parameters/action_set_predecessor_count.js b/src/legacy/core_plugins/kibana/public/discover/__tests__/query_parameters/action_set_predecessor_count.js
index 1ad4bdbea210d..9ba425bb0e489 100644
--- a/src/legacy/core_plugins/kibana/public/discover/__tests__/query_parameters/action_set_predecessor_count.js
+++ b/src/legacy/core_plugins/kibana/public/discover/__tests__/query_parameters/action_set_predecessor_count.js
@@ -19,7 +19,7 @@
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
-import { pluginInstance } from 'plugins/kibana/discover/index';
+import { pluginInstance } from 'plugins/kibana/discover/legacy';
import { createStateStub } from './_utils';
import { getQueryParameterActions } from '../../np_ready/angular/context/query_parameters/actions';
diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/query_parameters/action_set_query_parameters.js b/src/legacy/core_plugins/kibana/public/discover/__tests__/query_parameters/action_set_query_parameters.js
index e9ec2c300faa1..39dde2d8bb7cf 100644
--- a/src/legacy/core_plugins/kibana/public/discover/__tests__/query_parameters/action_set_query_parameters.js
+++ b/src/legacy/core_plugins/kibana/public/discover/__tests__/query_parameters/action_set_query_parameters.js
@@ -19,7 +19,7 @@
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
-import { pluginInstance } from 'plugins/kibana/discover/index';
+import { pluginInstance } from 'plugins/kibana/discover/legacy';
import { createStateStub } from './_utils';
import { getQueryParameterActions } from '../../np_ready/angular/context/query_parameters/actions';
diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/query_parameters/action_set_successor_count.js b/src/legacy/core_plugins/kibana/public/discover/__tests__/query_parameters/action_set_successor_count.js
index 15f3eefac3fd1..c05f5b4aff3bc 100644
--- a/src/legacy/core_plugins/kibana/public/discover/__tests__/query_parameters/action_set_successor_count.js
+++ b/src/legacy/core_plugins/kibana/public/discover/__tests__/query_parameters/action_set_successor_count.js
@@ -19,7 +19,7 @@
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
-import { pluginInstance } from 'plugins/kibana/discover/index';
+import { pluginInstance } from 'plugins/kibana/discover/legacy';
import { createStateStub } from './_utils';
import { getQueryParameterActions } from '../../np_ready/angular/context/query_parameters/actions';
diff --git a/src/legacy/core_plugins/kibana/public/discover/index.ts b/src/legacy/core_plugins/kibana/public/discover/index.ts
index 7bde30e0d0f0e..d851cb96a18c4 100644
--- a/src/legacy/core_plugins/kibana/public/discover/index.ts
+++ b/src/legacy/core_plugins/kibana/public/discover/index.ts
@@ -16,24 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
-import chrome from 'ui/chrome';
+
import { PluginInitializerContext } from 'kibana/public';
-import { npSetup, npStart } from 'ui/new_platform';
import { DiscoverPlugin } from './plugin';
+export { createSavedSearchesService } from './saved_searches/saved_searches';
+
// Core will be looking for this when loading our plugin in the new platform
export const plugin = (context: PluginInitializerContext) => {
return new DiscoverPlugin();
};
-
-// Legacy compatiblity part - to be removed at cutover, replaced by a kibana.json file
-export const pluginInstance = plugin({} as PluginInitializerContext);
-export const setup = pluginInstance.setup(npSetup.core, {
- ...npSetup.plugins,
- __LEGACY: {
- chrome,
- },
-});
-export const start = pluginInstance.start(npStart.core, npStart.plugins);
-
-export { createSavedSearchesService } from './saved_searches/saved_searches';
diff --git a/src/legacy/core_plugins/kibana/public/discover/legacy.ts b/src/legacy/core_plugins/kibana/public/discover/legacy.ts
new file mode 100644
index 0000000000000..2ec64177156f9
--- /dev/null
+++ b/src/legacy/core_plugins/kibana/public/discover/legacy.ts
@@ -0,0 +1,32 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import chrome from 'ui/chrome';
+import { PluginInitializerContext } from 'kibana/public';
+import { npSetup, npStart } from 'ui/new_platform';
+import { plugin } from './index';
+
+// Legacy compatiblity part - to be removed at cutover, replaced by a kibana.json file
+export const pluginInstance = plugin({} as PluginInitializerContext);
+export const setup = pluginInstance.setup(npSetup.core, {
+ ...npSetup.plugins,
+ __LEGACY: {
+ chrome,
+ },
+});
+export const start = pluginInstance.start(npStart.core, npStart.plugins);
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/__tests__/anchor.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/__tests__/anchor.js
index 45ce6cc9d0af2..debcccebbd11c 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/__tests__/anchor.js
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/__tests__/anchor.js
@@ -19,7 +19,7 @@
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
-import { pluginInstance } from 'plugins/kibana/discover/index';
+import { pluginInstance } from 'plugins/kibana/discover/legacy';
import { createIndexPatternsStub, createSearchSourceStub } from './_stubs';
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/__tests__/predecessors.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/__tests__/predecessors.js
index 266a505f6be14..c24b6ac6307ff 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/__tests__/predecessors.js
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/__tests__/predecessors.js
@@ -21,7 +21,7 @@ import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import moment from 'moment';
import * as _ from 'lodash';
-import { pluginInstance } from 'plugins/kibana/discover/index';
+import { pluginInstance } from 'plugins/kibana/discover/legacy';
import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs';
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/__tests__/successors.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/__tests__/successors.js
index e06d414ba260c..d4c00930c9383 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/__tests__/successors.js
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/__tests__/successors.js
@@ -21,7 +21,7 @@ import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import moment from 'moment';
import * as _ from 'lodash';
-import { pluginInstance } from 'plugins/kibana/discover/index';
+import { pluginInstance } from 'plugins/kibana/discover/legacy';
import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs';
diff --git a/src/legacy/core_plugins/kibana/public/home/index.ts b/src/legacy/core_plugins/kibana/public/home/index.ts
index b2d90f1444654..27d09a53ba20d 100644
--- a/src/legacy/core_plugins/kibana/public/home/index.ts
+++ b/src/legacy/core_plugins/kibana/public/home/index.ts
@@ -22,11 +22,8 @@ import { npSetup, npStart } from 'ui/new_platform';
import chrome from 'ui/chrome';
import { IPrivate } from 'ui/private';
import { HomePlugin, LegacyAngularInjectedDependencies } from './plugin';
-import { createUiStatsReporter, METRIC_TYPE } from '../../../ui_metric/public';
import { TelemetryOptInProvider } from '../../../telemetry/public/services';
-export const trackUiMetric = createUiStatsReporter('Kibana_home');
-
/**
* Get dependencies relying on the global angular context.
* They also have to get resolved together with the legacy imports above
@@ -54,9 +51,7 @@ let copiedLegacyCatalogue = false;
instance.setup(npSetup.core, {
...npSetup.plugins,
__LEGACY: {
- trackUiMetric,
metadata: npStart.core.injectedMetadata.getLegacyMetadata(),
- METRIC_TYPE,
getFeatureCatalogueEntries: async () => {
if (!copiedLegacyCatalogue) {
const injector = await chrome.dangerouslyGetActiveInjector();
diff --git a/src/legacy/core_plugins/kibana/public/home/kibana_services.ts b/src/legacy/core_plugins/kibana/public/home/kibana_services.ts
index 0eb55a3902eda..4d9177735556d 100644
--- a/src/legacy/core_plugins/kibana/public/home/kibana_services.ts
+++ b/src/legacy/core_plugins/kibana/public/home/kibana_services.ts
@@ -55,7 +55,6 @@ export interface HomeKibanaServices {
savedObjectsClient: SavedObjectsClientContract;
toastNotifications: NotificationsSetup['toasts'];
banners: OverlayStart['banners'];
- METRIC_TYPE: any;
trackUiMetric: (type: UiStatsMetricType, eventNames: string | string[], count?: number) => void;
getBasePath: () => string;
shouldShowTelemetryOptIn: boolean;
diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.test.tsx b/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.test.tsx
index 28bdab14193c4..55c469fa58fc6 100644
--- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.test.tsx
+++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.test.tsx
@@ -25,10 +25,6 @@ jest.mock('../../kibana_services', () => ({
getServices: () => ({
addBasePath: (path: string) => `root${path}`,
trackUiMetric: () => {},
- METRIC_TYPE: {
- LOADED: 'loaded',
- CLICK: 'click',
- },
}),
}));
diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.tsx b/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.tsx
index 9bbb7aaceb915..1b7761d068d2f 100644
--- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.tsx
+++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/welcome.tsx
@@ -35,6 +35,7 @@ import {
EuiIcon,
EuiPortal,
} from '@elastic/eui';
+import { METRIC_TYPE } from '@kbn/analytics';
import { FormattedMessage } from '@kbn/i18n/react';
import { getServices } from '../../kibana_services';
@@ -64,17 +65,17 @@ export class Welcome extends React.Component {
}
private onSampleDataDecline = () => {
- this.services.trackUiMetric(this.services.METRIC_TYPE.CLICK, 'sampleDataDecline');
+ this.services.trackUiMetric(METRIC_TYPE.CLICK, 'sampleDataDecline');
this.props.onSkip();
};
private onSampleDataConfirm = () => {
- this.services.trackUiMetric(this.services.METRIC_TYPE.CLICK, 'sampleDataConfirm');
+ this.services.trackUiMetric(METRIC_TYPE.CLICK, 'sampleDataConfirm');
this.redirecToSampleData();
};
componentDidMount() {
- this.services.trackUiMetric(this.services.METRIC_TYPE.LOADED, 'welcomeScreenMount');
+ this.services.trackUiMetric(METRIC_TYPE.LOADED, 'welcomeScreenMount');
this.props.onOptInSeen();
document.addEventListener('keydown', this.hideOnEsc);
}
diff --git a/src/legacy/core_plugins/kibana/public/home/plugin.ts b/src/legacy/core_plugins/kibana/public/home/plugin.ts
index 42ab049eb5b3a..502c8f45646cf 100644
--- a/src/legacy/core_plugins/kibana/public/home/plugin.ts
+++ b/src/legacy/core_plugins/kibana/public/home/plugin.ts
@@ -18,11 +18,11 @@
*/
import { CoreSetup, CoreStart, LegacyNavLink, Plugin, UiSettingsState } from 'kibana/public';
-import { UiStatsMetricType } from '@kbn/analytics';
import { DataPublicPluginStart } from 'src/plugins/data/public';
import { setServices } from './kibana_services';
import { KibanaLegacySetup } from '../../../../../plugins/kibana_legacy/public';
+import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/public';
import {
Environment,
FeatureCatalogueEntry,
@@ -41,8 +41,6 @@ export interface HomePluginStartDependencies {
export interface HomePluginSetupDependencies {
__LEGACY: {
- trackUiMetric: (type: UiStatsMetricType, eventNames: string | string[], count?: number) => void;
- METRIC_TYPE: any;
metadata: {
app: unknown;
bundleId: string;
@@ -59,6 +57,7 @@ export interface HomePluginSetupDependencies {
getFeatureCatalogueEntries: () => Promise;
getAngularDependencies: () => Promise;
};
+ usageCollection: UsageCollectionSetup;
kibana_legacy: KibanaLegacySetup;
}
@@ -71,6 +70,7 @@ export class HomePlugin implements Plugin {
core: CoreSetup,
{
kibana_legacy,
+ usageCollection,
__LEGACY: { getAngularDependencies, ...legacyServices },
}: HomePluginSetupDependencies
) {
@@ -78,9 +78,11 @@ export class HomePlugin implements Plugin {
id: 'home',
title: 'Home',
mount: async ({ core: contextCore }, params) => {
+ const trackUiMetric = usageCollection.reportUiStats.bind(usageCollection, 'Kibana_home');
const angularDependencies = await getAngularDependencies();
setServices({
...legacyServices,
+ trackUiMetric,
http: contextCore.http,
toastNotifications: core.notifications.toasts,
banners: contextCore.overlays.banners,
diff --git a/src/legacy/core_plugins/kibana/public/kibana.js b/src/legacy/core_plugins/kibana/public/kibana.js
index 4100ae7205869..bd947b9cb9d7f 100644
--- a/src/legacy/core_plugins/kibana/public/kibana.js
+++ b/src/legacy/core_plugins/kibana/public/kibana.js
@@ -47,9 +47,9 @@ import 'uiExports/interpreter';
import 'ui/autoload/all';
import 'ui/kbn_top_nav';
import './home';
-import './discover';
-import './visualize';
-import './dashboard';
+import './discover/legacy';
+import './visualize/legacy';
+import './dashboard/legacy';
import './management';
import './dev_tools';
import 'ui/color_maps';
diff --git a/src/legacy/core_plugins/kibana/public/visualize/index.ts b/src/legacy/core_plugins/kibana/public/visualize/index.ts
index f113c81256f8e..a39779792b83a 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/index.ts
+++ b/src/legacy/core_plugins/kibana/public/visualize/index.ts
@@ -17,50 +17,15 @@
* under the License.
*/
-import {
- IPrivate,
- legacyChrome,
- npSetup,
- npStart,
- VisEditorTypesRegistryProvider,
-} from './legacy_imports';
-import { VisualizePlugin, LegacyAngularInjectedDependencies } from './plugin';
-import { start as embeddables } from '../../../embeddable_api/public/np_ready/public/legacy';
-import { start as visualizations } from '../../../visualizations/public/np_ready/public/legacy';
+import { PluginInitializerContext } from 'kibana/public';
+import { VisualizePlugin } from './plugin';
export * from './np_ready/visualize_constants';
export { showNewVisModal } from './np_ready/wizard';
-/**
- * Get dependencies relying on the global angular context.
- * They also have to get resolved together with the legacy imports above
- */
-async function getAngularDependencies(): Promise {
- const injector = await legacyChrome.dangerouslyGetActiveInjector();
-
- const Private = injector.get('Private');
-
- const editorTypes = Private(VisEditorTypesRegistryProvider);
-
- return {
- legacyChrome,
- editorTypes,
- };
-}
-
-(() => {
- const instance = new VisualizePlugin();
- instance.setup(npSetup.core, {
- ...npSetup.plugins,
- __LEGACY: {
- getAngularDependencies,
- },
- });
- instance.start(npStart.core, {
- ...npStart.plugins,
- embeddables,
- visualizations,
- });
-})();
-
export { createSavedVisLoader } from './saved_visualizations/saved_visualizations';
+
+// Core will be looking for this when loading our plugin in the new platform
+export const plugin = (context: PluginInitializerContext) => {
+ return new VisualizePlugin();
+};
diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy.ts b/src/legacy/core_plugins/kibana/public/visualize/legacy.ts
new file mode 100644
index 0000000000000..2a1b6130fac89
--- /dev/null
+++ b/src/legacy/core_plugins/kibana/public/visualize/legacy.ts
@@ -0,0 +1,65 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import 'ui/collapsible_sidebar'; // used in default editor
+
+import { PluginInitializerContext } from 'kibana/public';
+import {
+ IPrivate,
+ legacyChrome,
+ npSetup,
+ npStart,
+ VisEditorTypesRegistryProvider,
+} from './legacy_imports';
+import { LegacyAngularInjectedDependencies } from './plugin';
+import { start as embeddables } from '../../../embeddable_api/public/np_ready/public/legacy';
+import { start as visualizations } from '../../../visualizations/public/np_ready/public/legacy';
+import { plugin } from './index';
+
+/**
+ * Get dependencies relying on the global angular context.
+ * They also have to get resolved together with the legacy imports above
+ */
+async function getAngularDependencies(): Promise {
+ const injector = await legacyChrome.dangerouslyGetActiveInjector();
+
+ const Private = injector.get('Private');
+
+ const editorTypes = Private(VisEditorTypesRegistryProvider);
+
+ return {
+ legacyChrome,
+ editorTypes,
+ };
+}
+
+(() => {
+ const instance = plugin({} as PluginInitializerContext);
+ instance.setup(npSetup.core, {
+ ...npSetup.plugins,
+ __LEGACY: {
+ getAngularDependencies,
+ },
+ });
+ instance.start(npStart.core, {
+ ...npStart.plugins,
+ embeddables,
+ visualizations,
+ });
+})();
diff --git a/src/legacy/ui/public/directives/__tests__/css_truncate.js b/src/legacy/ui/public/directives/__tests__/css_truncate.js
index 9d470c10358cc..bf102f5a29fdb 100644
--- a/src/legacy/ui/public/directives/__tests__/css_truncate.js
+++ b/src/legacy/ui/public/directives/__tests__/css_truncate.js
@@ -20,7 +20,7 @@
import angular from 'angular';
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
-import 'plugins/kibana/discover/index';
+import 'plugins/kibana/discover/legacy';
let $parentScope;
diff --git a/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js b/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js
index 391973f6d909b..fbf917054edbf 100644
--- a/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js
+++ b/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js
@@ -34,6 +34,7 @@ import 'ui/color_maps';
import 'ui/agg_response';
import 'ui/agg_types';
import 'leaflet';
+import 'plugins/kibana/dashboard/legacy';
import { npStart } from 'ui/new_platform';
import { localApplicationService } from 'plugins/kibana/local_application_service';
From 6b02ed804fa858447f1e69627ccf6c121358dcab Mon Sep 17 00:00:00 2001
From: Joe Reuter
Date: Tue, 21 Jan 2020 14:52:32 +0100
Subject: [PATCH 007/187] Migrate session storage short url handling (#55021)
---
.../state_session_storage_redirect/index.js | 32 -------
.../package.json | 5 --
src/legacy/server/kbn_server.js | 4 -
src/legacy/server/url_shortening/index.js | 20 -----
.../routes/lib/short_url_assert_valid.js | 41 ---------
.../routes/lib/short_url_assert_valid.test.js | 63 --------------
.../routes/lib/short_url_error.js | 26 ------
.../routes/lib/short_url_error.test.js | 67 ---------------
.../routes/lib/short_url_lookup.js | 43 ----------
.../routes/lib/short_url_lookup.test.js | 84 -------------------
.../url_shortening/url_shortening_mixin.js | 23 -----
src/plugins/newsfeed/public/plugin.tsx | 12 ++-
.../share/common/short_url_routes.ts} | 16 ++--
src/plugins/share/public/lib/url_shortener.ts | 5 +-
src/plugins/share/public/plugin.test.ts | 17 +++-
src/plugins/share/public/plugin.ts | 6 +-
.../services/short_url_redirect_app.test.ts | 46 ++++++++++
.../services/short_url_redirect_app.ts} | 39 +++++----
.../share/server/routes/create_routes.ts | 2 +
.../share/server/routes/get.ts} | 47 +++++++----
src/plugins/share/server/routes/goto.ts | 10 ++-
.../share/server/routes/shorten_url.ts | 3 +-
22 files changed, 151 insertions(+), 460 deletions(-)
delete mode 100644 src/legacy/core_plugins/state_session_storage_redirect/index.js
delete mode 100644 src/legacy/core_plugins/state_session_storage_redirect/package.json
delete mode 100644 src/legacy/server/url_shortening/index.js
delete mode 100644 src/legacy/server/url_shortening/routes/lib/short_url_assert_valid.js
delete mode 100644 src/legacy/server/url_shortening/routes/lib/short_url_assert_valid.test.js
delete mode 100644 src/legacy/server/url_shortening/routes/lib/short_url_error.js
delete mode 100644 src/legacy/server/url_shortening/routes/lib/short_url_error.test.js
delete mode 100644 src/legacy/server/url_shortening/routes/lib/short_url_lookup.js
delete mode 100644 src/legacy/server/url_shortening/routes/lib/short_url_lookup.test.js
delete mode 100644 src/legacy/server/url_shortening/url_shortening_mixin.js
rename src/{legacy/server/url_shortening/routes/create_routes.js => plugins/share/common/short_url_routes.ts} (67%)
create mode 100644 src/plugins/share/public/services/short_url_redirect_app.test.ts
rename src/{legacy/core_plugins/state_session_storage_redirect/public/index.js => plugins/share/public/services/short_url_redirect_app.ts} (50%)
rename src/{legacy/server/url_shortening/routes/goto.js => plugins/share/server/routes/get.ts} (55%)
diff --git a/src/legacy/core_plugins/state_session_storage_redirect/index.js b/src/legacy/core_plugins/state_session_storage_redirect/index.js
deleted file mode 100644
index 2d4d7c97232c0..0000000000000
--- a/src/legacy/core_plugins/state_session_storage_redirect/index.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-export default function(kibana) {
- return new kibana.Plugin({
- uiExports: {
- app: {
- require: ['kibana'],
- title: 'Redirecting',
- id: 'stateSessionStorageRedirect',
- main: 'plugins/state_session_storage_redirect',
- hidden: true,
- },
- },
- });
-}
diff --git a/src/legacy/core_plugins/state_session_storage_redirect/package.json b/src/legacy/core_plugins/state_session_storage_redirect/package.json
deleted file mode 100644
index 21956e5d76d5b..0000000000000
--- a/src/legacy/core_plugins/state_session_storage_redirect/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "state_session_storage_redirect",
- "version": "kibana",
- "description": "When using the state:storeInSessionStorage setting with the short-urls, we need some way to get the full URL's hashed states into sessionStorage, this app will grab the URL from the injected state and and put the URL hashed states into sessionStorage before redirecting the user."
-}
diff --git a/src/legacy/server/kbn_server.js b/src/legacy/server/kbn_server.js
index 8ab55d4c517b5..6991527a9503c 100644
--- a/src/legacy/server/kbn_server.js
+++ b/src/legacy/server/kbn_server.js
@@ -36,7 +36,6 @@ import * as Plugins from './plugins';
import { indexPatternsMixin } from './index_patterns';
import { savedObjectsMixin } from './saved_objects/saved_objects_mixin';
import { capabilitiesMixin } from './capabilities';
-import { urlShorteningMixin } from './url_shortening';
import { serverExtensionsMixin } from './server_extensions';
import { uiMixin } from '../ui';
import { sassMixin } from './sass';
@@ -123,9 +122,6 @@ export default class KbnServer {
// setup capabilities routes
capabilitiesMixin,
- // setup routes for short urls
- urlShorteningMixin,
-
// ensure that all bundles are built, or that the
// watch bundle server is running
optimizeMixin,
diff --git a/src/legacy/server/url_shortening/index.js b/src/legacy/server/url_shortening/index.js
deleted file mode 100644
index 031af0618d7bc..0000000000000
--- a/src/legacy/server/url_shortening/index.js
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-export { urlShorteningMixin } from './url_shortening_mixin';
diff --git a/src/legacy/server/url_shortening/routes/lib/short_url_assert_valid.js b/src/legacy/server/url_shortening/routes/lib/short_url_assert_valid.js
deleted file mode 100644
index ff2b0f214e5ee..0000000000000
--- a/src/legacy/server/url_shortening/routes/lib/short_url_assert_valid.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { parse } from 'url';
-import { trim } from 'lodash';
-import Boom from 'boom';
-
-export function shortUrlAssertValid(url) {
- const { protocol, hostname, pathname } = parse(url);
-
- if (protocol) {
- throw Boom.notAcceptable(`Short url targets cannot have a protocol, found "${protocol}"`);
- }
-
- if (hostname) {
- throw Boom.notAcceptable(`Short url targets cannot have a hostname, found "${hostname}"`);
- }
-
- const pathnameParts = trim(pathname, '/').split('/');
- if (pathnameParts.length !== 2) {
- throw Boom.notAcceptable(
- `Short url target path must be in the format "/app/{{appId}}", found "${pathname}"`
- );
- }
-}
diff --git a/src/legacy/server/url_shortening/routes/lib/short_url_assert_valid.test.js b/src/legacy/server/url_shortening/routes/lib/short_url_assert_valid.test.js
deleted file mode 100644
index f83073e6aefe9..0000000000000
--- a/src/legacy/server/url_shortening/routes/lib/short_url_assert_valid.test.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { shortUrlAssertValid } from './short_url_assert_valid';
-
-describe('shortUrlAssertValid()', () => {
- const invalid = [
- ['protocol', 'http://localhost:5601/app/kibana'],
- ['protocol', 'https://localhost:5601/app/kibana'],
- ['protocol', 'mailto:foo@bar.net'],
- ['protocol', 'javascript:alert("hi")'], // eslint-disable-line no-script-url
- ['hostname', 'localhost/app/kibana'],
- ['hostname and port', 'local.host:5601/app/kibana'],
- ['hostname and auth', 'user:pass@localhost.net/app/kibana'],
- ['path traversal', '/app/../../not-kibana'],
- ['deep path', '/app/kibana/foo'],
- ['deep path', '/app/kibana/foo/bar'],
- ['base path', '/base/app/kibana'],
- ];
-
- invalid.forEach(([desc, url]) => {
- it(`fails when url has ${desc}`, () => {
- try {
- shortUrlAssertValid(url);
- throw new Error(`expected assertion to throw`);
- } catch (err) {
- if (!err || !err.isBoom) {
- throw err;
- }
- }
- });
- });
-
- const valid = [
- '/app/kibana',
- '/app/monitoring#angular/route',
- '/app/text#document-id',
- '/app/some?with=query',
- '/app/some?with=query#and-a-hash',
- ];
-
- valid.forEach(url => {
- it(`allows ${url}`, () => {
- shortUrlAssertValid(url);
- });
- });
-});
diff --git a/src/legacy/server/url_shortening/routes/lib/short_url_error.js b/src/legacy/server/url_shortening/routes/lib/short_url_error.js
deleted file mode 100644
index ed44ba21aa4c4..0000000000000
--- a/src/legacy/server/url_shortening/routes/lib/short_url_error.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import Boom from 'boom';
-
-export function handleShortUrlError(error) {
- return Boom.boomify(error, {
- statusCode: error.statusCode || error.status || 500,
- });
-}
diff --git a/src/legacy/server/url_shortening/routes/lib/short_url_error.test.js b/src/legacy/server/url_shortening/routes/lib/short_url_error.test.js
deleted file mode 100644
index 4eca6320ec834..0000000000000
--- a/src/legacy/server/url_shortening/routes/lib/short_url_error.test.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import _ from 'lodash';
-import { handleShortUrlError } from './short_url_error';
-
-function createErrorWithStatus(status) {
- const error = new Error();
- error.status = status;
- return error;
-}
-
-function createErrorWithStatusCode(statusCode) {
- const error = new Error();
- error.statusCode = statusCode;
- return error;
-}
-
-describe('handleShortUrlError()', () => {
- const caughtErrorsWithStatus = [
- createErrorWithStatus(401),
- createErrorWithStatus(403),
- createErrorWithStatus(404),
- ];
-
- const caughtErrorsWithStatusCode = [
- createErrorWithStatusCode(401),
- createErrorWithStatusCode(403),
- createErrorWithStatusCode(404),
- ];
-
- const uncaughtErrors = [new Error(), createErrorWithStatus(500), createErrorWithStatusCode(500)];
-
- caughtErrorsWithStatus.forEach(err => {
- it(`should handle errors with status of ${err.status}`, function() {
- expect(_.get(handleShortUrlError(err), 'output.statusCode')).toBe(err.status);
- });
- });
-
- caughtErrorsWithStatusCode.forEach(err => {
- it(`should handle errors with statusCode of ${err.statusCode}`, function() {
- expect(_.get(handleShortUrlError(err), 'output.statusCode')).toBe(err.statusCode);
- });
- });
-
- uncaughtErrors.forEach(err => {
- it(`should not handle unknown errors`, function() {
- expect(_.get(handleShortUrlError(err), 'output.statusCode')).toBe(500);
- });
- });
-});
diff --git a/src/legacy/server/url_shortening/routes/lib/short_url_lookup.js b/src/legacy/server/url_shortening/routes/lib/short_url_lookup.js
deleted file mode 100644
index a8a01d1433a7a..0000000000000
--- a/src/legacy/server/url_shortening/routes/lib/short_url_lookup.js
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { get } from 'lodash';
-
-export function shortUrlLookupProvider(server) {
- async function updateMetadata(doc, req) {
- try {
- await req.getSavedObjectsClient().update('url', doc.id, {
- accessDate: new Date(),
- accessCount: get(doc, 'attributes.accessCount', 0) + 1,
- });
- } catch (err) {
- server.log('Warning: Error updating url metadata', err);
- //swallow errors. It isn't critical if there is no update.
- }
- }
-
- return {
- async getUrl(id, req) {
- const doc = await req.getSavedObjectsClient().get('url', id);
- updateMetadata(doc, req);
-
- return doc.attributes.url;
- },
- };
-}
diff --git a/src/legacy/server/url_shortening/routes/lib/short_url_lookup.test.js b/src/legacy/server/url_shortening/routes/lib/short_url_lookup.test.js
deleted file mode 100644
index e8bf72a142d11..0000000000000
--- a/src/legacy/server/url_shortening/routes/lib/short_url_lookup.test.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import sinon from 'sinon';
-import { shortUrlLookupProvider } from './short_url_lookup';
-import { SavedObjectsClient } from '../../../../../core/server';
-
-describe('shortUrlLookupProvider', () => {
- const ID = 'bf00ad16941fc51420f91a93428b27a0';
- const TYPE = 'url';
- const URL = 'http://elastic.co';
- const server = { log: sinon.stub() };
- const sandbox = sinon.createSandbox();
-
- let savedObjectsClient;
- let req;
- let shortUrl;
-
- beforeEach(() => {
- savedObjectsClient = {
- get: sandbox.stub(),
- create: sandbox.stub().returns(Promise.resolve({ id: ID })),
- update: sandbox.stub(),
- errors: SavedObjectsClient.errors,
- };
-
- req = { getSavedObjectsClient: () => savedObjectsClient };
- shortUrl = shortUrlLookupProvider(server);
- });
-
- afterEach(() => {
- sandbox.restore();
- });
-
- describe('getUrl', () => {
- beforeEach(() => {
- const attributes = { accessCount: 2, url: URL };
- savedObjectsClient.get.returns({ id: ID, attributes });
- });
-
- it('provides the ID to savedObjectsClient', async () => {
- await shortUrl.getUrl(ID, req);
-
- sinon.assert.calledOnce(savedObjectsClient.get);
- const [type, id] = savedObjectsClient.get.getCall(0).args;
-
- expect(type).toEqual(TYPE);
- expect(id).toEqual(ID);
- });
-
- it('returns the url', async () => {
- const response = await shortUrl.getUrl(ID, req);
- expect(response).toEqual(URL);
- });
-
- it('increments accessCount', async () => {
- await shortUrl.getUrl(ID, req);
-
- sinon.assert.calledOnce(savedObjectsClient.update);
- const [type, id, attributes] = savedObjectsClient.update.getCall(0).args;
-
- expect(type).toEqual(TYPE);
- expect(id).toEqual(ID);
- expect(Object.keys(attributes).sort()).toEqual(['accessCount', 'accessDate']);
- expect(attributes.accessCount).toEqual(3);
- });
- });
-});
diff --git a/src/legacy/server/url_shortening/url_shortening_mixin.js b/src/legacy/server/url_shortening/url_shortening_mixin.js
deleted file mode 100644
index 867898cac845a..0000000000000
--- a/src/legacy/server/url_shortening/url_shortening_mixin.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import { createRoutes } from './routes/create_routes';
-
-export function urlShorteningMixin(kbnServer, server) {
- createRoutes(server);
-}
diff --git a/src/plugins/newsfeed/public/plugin.tsx b/src/plugins/newsfeed/public/plugin.tsx
index 5ea5e5b324717..c4e042fe452f9 100644
--- a/src/plugins/newsfeed/public/plugin.tsx
+++ b/src/plugins/newsfeed/public/plugin.tsx
@@ -23,7 +23,7 @@ import ReactDOM from 'react-dom';
import React from 'react';
import { I18nProvider } from '@kbn/i18n/react';
import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public';
-import { NewsfeedPluginInjectedConfig } from '../types';
+import { FetchResult, NewsfeedPluginInjectedConfig } from '../types';
import { NewsfeedNavButton, NewsfeedApiFetchResult } from './components/newsfeed_header_nav_button';
import { getApi } from './lib/api';
@@ -54,10 +54,14 @@ export class NewsfeedPublicPlugin implements Plugin {
private fetchNewsfeed(core: CoreStart) {
const { http, injectedMetadata } = core;
- const config = injectedMetadata.getInjectedVar(
- 'newsfeed'
- ) as NewsfeedPluginInjectedConfig['newsfeed'];
+ const config = injectedMetadata.getInjectedVar('newsfeed') as
+ | NewsfeedPluginInjectedConfig['newsfeed']
+ | undefined;
+ if (!config) {
+ // running in new platform, injected metadata not available
+ return new Rx.Observable();
+ }
return getApi(http, config, this.kibanaVersion).pipe(
takeUntil(this.stop$), // stop the interval when stop method is called
catchError(() => Rx.of(null)) // do not throw error
diff --git a/src/legacy/server/url_shortening/routes/create_routes.js b/src/plugins/share/common/short_url_routes.ts
similarity index 67%
rename from src/legacy/server/url_shortening/routes/create_routes.js
rename to src/plugins/share/common/short_url_routes.ts
index 9540e7441a268..7b42534de2ab1 100644
--- a/src/legacy/server/url_shortening/routes/create_routes.js
+++ b/src/plugins/share/common/short_url_routes.ts
@@ -17,11 +17,15 @@
* under the License.
*/
-import { shortUrlLookupProvider } from './lib/short_url_lookup';
-import { createGotoRoute } from './goto';
+export const GOTO_PREFIX = '/goto';
-export function createRoutes(server) {
- const shortUrlLookup = shortUrlLookupProvider(server);
+export const getUrlIdFromGotoRoute = (path: string) =>
+ path.match(new RegExp(`${GOTO_PREFIX}/(.*)$`))?.[1];
- server.route(createGotoRoute({ server, shortUrlLookup }));
-}
+export const getGotoPath = (urlId: string) => `${GOTO_PREFIX}/${urlId}`;
+
+export const GETTER_PREFIX = '/api/short_url';
+
+export const getUrlPath = (urlId: string) => `${GETTER_PREFIX}/${urlId}`;
+
+export const CREATE_PATH = '/api/shorten_url';
diff --git a/src/plugins/share/public/lib/url_shortener.ts b/src/plugins/share/public/lib/url_shortener.ts
index 29d91bdb1aae6..f2259f1fabf7d 100644
--- a/src/plugins/share/public/lib/url_shortener.ts
+++ b/src/plugins/share/public/lib/url_shortener.ts
@@ -19,6 +19,7 @@
import url from 'url';
import { HttpStart } from 'kibana/public';
+import { CREATE_PATH, getGotoPath } from '../../common/short_url_routes';
export async function shortenUrl(
absoluteUrl: string,
@@ -34,10 +35,10 @@ export async function shortenUrl(
const body = JSON.stringify({ url: relativeUrl });
- const resp = await post('/api/shorten_url', { body });
+ const resp = await post(CREATE_PATH, { body });
return url.format({
protocol: parsedUrl.protocol,
host: parsedUrl.host,
- pathname: `${basePath}/goto/${resp.urlId}`,
+ pathname: `${basePath}${getGotoPath(resp.urlId)}`,
});
}
diff --git a/src/plugins/share/public/plugin.test.ts b/src/plugins/share/public/plugin.test.ts
index 5610490be33b3..730814fe9ed23 100644
--- a/src/plugins/share/public/plugin.test.ts
+++ b/src/plugins/share/public/plugin.test.ts
@@ -20,6 +20,7 @@
import { registryMock, managerMock } from './plugin.test.mocks';
import { SharePlugin } from './plugin';
import { CoreStart } from 'kibana/public';
+import { coreMock } from '../../../core/public/mocks';
describe('SharePlugin', () => {
beforeEach(() => {
@@ -30,16 +31,28 @@ describe('SharePlugin', () => {
describe('setup', () => {
test('wires up and returns registry', async () => {
- const setup = await new SharePlugin().setup();
+ const coreSetup = coreMock.createSetup();
+ const setup = await new SharePlugin().setup(coreSetup);
expect(registryMock.setup).toHaveBeenCalledWith();
expect(setup.register).toBeDefined();
});
+
+ test('registers redirect app', async () => {
+ const coreSetup = coreMock.createSetup();
+ await new SharePlugin().setup(coreSetup);
+ expect(coreSetup.application.register).toHaveBeenCalledWith(
+ expect.objectContaining({
+ id: 'short_url_redirect',
+ })
+ );
+ });
});
describe('start', () => {
test('wires up and returns show function, but not registry', async () => {
+ const coreSetup = coreMock.createSetup();
const service = new SharePlugin();
- await service.setup();
+ await service.setup(coreSetup);
const start = await service.start({} as CoreStart);
expect(registryMock.start).toHaveBeenCalled();
expect(managerMock.start).toHaveBeenCalledWith(
diff --git a/src/plugins/share/public/plugin.ts b/src/plugins/share/public/plugin.ts
index 6d78211cf9954..01c248624950a 100644
--- a/src/plugins/share/public/plugin.ts
+++ b/src/plugins/share/public/plugin.ts
@@ -17,15 +17,17 @@
* under the License.
*/
-import { CoreStart, Plugin } from 'src/core/public';
+import { CoreSetup, CoreStart, Plugin } from 'src/core/public';
import { ShareMenuManager, ShareMenuManagerStart } from './services';
import { ShareMenuRegistry, ShareMenuRegistrySetup } from './services';
+import { createShortUrlRedirectApp } from './services/short_url_redirect_app';
export class SharePlugin implements Plugin {
private readonly shareMenuRegistry = new ShareMenuRegistry();
private readonly shareContextMenu = new ShareMenuManager();
- public async setup() {
+ public async setup(core: CoreSetup) {
+ core.application.register(createShortUrlRedirectApp(core, window.location));
return {
...this.shareMenuRegistry.setup(),
};
diff --git a/src/plugins/share/public/services/short_url_redirect_app.test.ts b/src/plugins/share/public/services/short_url_redirect_app.test.ts
new file mode 100644
index 0000000000000..206e637451ec0
--- /dev/null
+++ b/src/plugins/share/public/services/short_url_redirect_app.test.ts
@@ -0,0 +1,46 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { createShortUrlRedirectApp } from './short_url_redirect_app';
+import { coreMock } from '../../../../core/public/mocks';
+import { hashUrl } from '../../../kibana_utils/public';
+
+jest.mock('../../../kibana_utils/public', () => ({ hashUrl: jest.fn(x => `${x}/hashed`) }));
+
+describe('short_url_redirect_app', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should fetch url and redirect to hashed version', async () => {
+ const coreSetup = coreMock.createSetup({ basePath: 'base' });
+ coreSetup.http.get.mockResolvedValueOnce({ url: '/app/abc' });
+ const locationMock = { pathname: '/base/goto/12345', href: '' } as Location;
+
+ const { mount } = createShortUrlRedirectApp(coreSetup, locationMock);
+ await mount();
+
+ // check for fetching the complete URL
+ expect(coreSetup.http.get).toHaveBeenCalledWith('/api/short_url/12345');
+ // check for hashing the URL returned from the server
+ expect(hashUrl).toHaveBeenCalledWith('/app/abc');
+ // check for redirecting to the prepended path
+ expect(locationMock.href).toEqual('base/app/abc/hashed');
+ });
+});
diff --git a/src/legacy/core_plugins/state_session_storage_redirect/public/index.js b/src/plugins/share/public/services/short_url_redirect_app.ts
similarity index 50%
rename from src/legacy/core_plugins/state_session_storage_redirect/public/index.js
rename to src/plugins/share/public/services/short_url_redirect_app.ts
index 701a5736c7d3b..6f72b711f6602 100644
--- a/src/legacy/core_plugins/state_session_storage_redirect/public/index.js
+++ b/src/plugins/share/public/services/short_url_redirect_app.ts
@@ -17,24 +17,29 @@
* under the License.
*/
-import chrome from 'ui/chrome';
-import { hashUrl } from '../../../../plugins/kibana_utils/public';
-import uiRoutes from 'ui/routes';
-import { fatalError } from 'ui/notify';
+import { CoreSetup } from 'kibana/public';
+import { getUrlIdFromGotoRoute, getUrlPath, GOTO_PREFIX } from '../../common/short_url_routes';
+import { hashUrl } from '../../../kibana_utils/public';
-uiRoutes.enable();
-uiRoutes.when('/', {
- resolve: {
- url: function(AppState, globalState, $window) {
- const redirectUrl = chrome.getInjected('redirectUrl');
- try {
- const hashedUrl = hashUrl(redirectUrl);
- const url = chrome.addBasePath(hashedUrl);
+export const createShortUrlRedirectApp = (core: CoreSetup, location: Location) => ({
+ id: 'short_url_redirect',
+ appRoute: GOTO_PREFIX,
+ chromeless: true,
+ title: 'Short URL Redirect',
+ async mount() {
+ const urlId = getUrlIdFromGotoRoute(location.pathname);
- $window.location = url;
- } catch (e) {
- fatalError(e);
- }
- },
+ if (!urlId) {
+ throw new Error('Url id not present in path');
+ }
+
+ const response = await core.http.get<{ url: string }>(getUrlPath(urlId));
+ const redirectUrl = response.url;
+ const hashedUrl = hashUrl(redirectUrl);
+ const url = core.http.basePath.prepend(hashedUrl);
+
+ location.href = url;
+
+ return () => {};
},
});
diff --git a/src/plugins/share/server/routes/create_routes.ts b/src/plugins/share/server/routes/create_routes.ts
index bd4b6fdb08791..22d10c25197c9 100644
--- a/src/plugins/share/server/routes/create_routes.ts
+++ b/src/plugins/share/server/routes/create_routes.ts
@@ -22,11 +22,13 @@ import { CoreSetup, Logger } from 'kibana/server';
import { shortUrlLookupProvider } from './lib/short_url_lookup';
import { createGotoRoute } from './goto';
import { createShortenUrlRoute } from './shorten_url';
+import { createGetterRoute } from './get';
export function createRoutes({ http }: CoreSetup, logger: Logger) {
const shortUrlLookup = shortUrlLookupProvider({ logger });
const router = http.createRouter();
createGotoRoute({ router, shortUrlLookup, http });
+ createGetterRoute({ router, shortUrlLookup, http });
createShortenUrlRoute({ router, shortUrlLookup });
}
diff --git a/src/legacy/server/url_shortening/routes/goto.js b/src/plugins/share/server/routes/get.ts
similarity index 55%
rename from src/legacy/server/url_shortening/routes/goto.js
rename to src/plugins/share/server/routes/get.ts
index 7a874786423d8..d6b191341dbe1 100644
--- a/src/legacy/server/url_shortening/routes/goto.js
+++ b/src/plugins/share/server/routes/get.ts
@@ -17,23 +17,40 @@
* under the License.
*/
-import { handleShortUrlError } from './lib/short_url_error';
+import { CoreSetup, IRouter } from 'kibana/server';
+import { schema } from '@kbn/config-schema';
+
import { shortUrlAssertValid } from './lib/short_url_assert_valid';
+import { ShortUrlLookupService } from './lib/short_url_lookup';
+import { getUrlPath } from '../../common/short_url_routes';
-export const createGotoRoute = ({ server, shortUrlLookup }) => ({
- method: 'GET',
- path: '/goto_LP/{urlId}',
- handler: async function(request, h) {
- try {
- const url = await shortUrlLookup.getUrl(request.params.urlId, request);
+export const createGetterRoute = ({
+ router,
+ shortUrlLookup,
+ http,
+}: {
+ router: IRouter;
+ shortUrlLookup: ShortUrlLookupService;
+ http: CoreSetup['http'];
+}) => {
+ router.get(
+ {
+ path: getUrlPath('{urlId}'),
+ validate: {
+ params: schema.object({ urlId: schema.string() }),
+ },
+ },
+ router.handleLegacyErrors(async function(context, request, response) {
+ const url = await shortUrlLookup.getUrl(request.params.urlId, {
+ savedObjects: context.core.savedObjects.client,
+ });
shortUrlAssertValid(url);
- const app = server.getHiddenUiAppById('stateSessionStorageRedirect');
- return h.renderApp(app, {
- redirectUrl: url,
+ return response.ok({
+ body: {
+ url,
+ },
});
- } catch (err) {
- throw handleShortUrlError(err);
- }
- },
-});
+ })
+ );
+};
diff --git a/src/plugins/share/server/routes/goto.ts b/src/plugins/share/server/routes/goto.ts
index 7343dc1bd34a2..5c3a4e441099f 100644
--- a/src/plugins/share/server/routes/goto.ts
+++ b/src/plugins/share/server/routes/goto.ts
@@ -22,6 +22,7 @@ import { schema } from '@kbn/config-schema';
import { shortUrlAssertValid } from './lib/short_url_assert_valid';
import { ShortUrlLookupService } from './lib/short_url_lookup';
+import { getGotoPath } from '../../common/short_url_routes';
export const createGotoRoute = ({
router,
@@ -34,7 +35,7 @@ export const createGotoRoute = ({
}) => {
router.get(
{
- path: '/goto/{urlId}',
+ path: getGotoPath('{urlId}'),
validate: {
params: schema.object({ urlId: schema.string() }),
},
@@ -54,10 +55,13 @@ export const createGotoRoute = ({
},
});
}
- return response.redirected({
+ const body = await context.core.rendering.render();
+
+ return response.ok({
headers: {
- location: http.basePath.prepend('/goto_LP/' + request.params.urlId),
+ 'content-security-policy': http.csp.header,
},
+ body,
});
})
);
diff --git a/src/plugins/share/server/routes/shorten_url.ts b/src/plugins/share/server/routes/shorten_url.ts
index 116b90c6971c5..41570f8a5f453 100644
--- a/src/plugins/share/server/routes/shorten_url.ts
+++ b/src/plugins/share/server/routes/shorten_url.ts
@@ -22,6 +22,7 @@ import { schema } from '@kbn/config-schema';
import { shortUrlAssertValid } from './lib/short_url_assert_valid';
import { ShortUrlLookupService } from './lib/short_url_lookup';
+import { CREATE_PATH } from '../../common/short_url_routes';
export const createShortenUrlRoute = ({
shortUrlLookup,
@@ -32,7 +33,7 @@ export const createShortenUrlRoute = ({
}) => {
router.post(
{
- path: '/api/shorten_url',
+ path: CREATE_PATH,
validate: {
body: schema.object({ url: schema.string() }),
},
From d3cef4791ec88da7d364596051e6f86310a800dd Mon Sep 17 00:00:00 2001
From: Nathan Reese
Date: Tue, 21 Jan 2020 08:54:42 -0500
Subject: [PATCH 008/187] [Maps] fix warning about missing key in react element
(#55372)
---
.../legacy/plugins/maps/public/layers/styles/color_utils.js | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js b/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js
index df212f23cd894..b04f8ff56e5ee 100644
--- a/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js
+++ b/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js
@@ -113,7 +113,11 @@ export const COLOR_PALETTES = COLOR_PALETTES_CONFIGS.map(palette => {
height: '100%',
display: 'inline-block',
};
- return
;
+ return (
+
+
+
+ );
});
return {
value: palette.id,
From 6feabcd53342482d403247487dfc7bb6ca6e74d9 Mon Sep 17 00:00:00 2001
From: Daniil Suleiman <31325372+sulemanof@users.noreply.github.com>
Date: Tue, 21 Jan 2020 17:38:28 +0300
Subject: [PATCH 009/187] Generate a static parser, move tests to
vis_type_timelion (#55299)
* Use generated parser, move tests to vis_type_timelion
* Remove legacy tests
* Create a grunt task for generating a parser
---
.eslintignore | 1 +
.../core_plugins/vis_type_timelion/README.md | 10 +
.../public/_generated_/chain.js | 1780 +++++++++++++++++
...timelion_expression_input_helpers.test.ts} | 172 +-
.../timelion_expression_input_helpers.ts | 13 +-
tasks/config/peg.js | 4 +
6 files changed, 1843 insertions(+), 137 deletions(-)
create mode 100644 src/legacy/core_plugins/vis_type_timelion/README.md
create mode 100644 src/legacy/core_plugins/vis_type_timelion/public/_generated_/chain.js
rename src/legacy/core_plugins/{timelion/public/directives/__tests__/timelion_expression_input_helpers.js => vis_type_timelion/public/components/timelion_expression_input_helpers.test.ts} (70%)
diff --git a/.eslintignore b/.eslintignore
index c4fb806b6d394..86a01b68ecab1 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -9,6 +9,7 @@ bower_components
/built_assets
/html_docs
/src/plugins/data/common/es_query/kuery/ast/_generated_/**
+/src/legacy/core_plugins/vis_type_timelion/public/_generated_/**
src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data
/src/legacy/ui/public/angular-bootstrap
/src/legacy/ui/public/flot-charts
diff --git a/src/legacy/core_plugins/vis_type_timelion/README.md b/src/legacy/core_plugins/vis_type_timelion/README.md
new file mode 100644
index 0000000000000..c306e03abf2c6
--- /dev/null
+++ b/src/legacy/core_plugins/vis_type_timelion/README.md
@@ -0,0 +1,10 @@
+# Vis type Timelion
+
+# Generate a parser
+If your grammar was changed in `public/chain.peg` you need to re-generate the static parser. You could use a grunt task:
+
+```
+grunt peg:timelion_chain
+```
+
+The generated parser will be appeared at `public/_generated_` folder, which is included in `.eslintignore`
\ No newline at end of file
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/_generated_/chain.js b/src/legacy/core_plugins/vis_type_timelion/public/_generated_/chain.js
new file mode 100644
index 0000000000000..f812b94238d43
--- /dev/null
+++ b/src/legacy/core_plugins/vis_type_timelion/public/_generated_/chain.js
@@ -0,0 +1,1780 @@
+module.exports = (function() {
+ "use strict";
+
+ /*
+ * Generated by PEG.js 0.9.0.
+ *
+ * http://pegjs.org/
+ */
+
+ function peg$subclass(child, parent) {
+ function ctor() { this.constructor = child; }
+ ctor.prototype = parent.prototype;
+ child.prototype = new ctor();
+ }
+
+ function peg$SyntaxError(message, expected, found, location) {
+ this.message = message;
+ this.expected = expected;
+ this.found = found;
+ this.location = location;
+ this.name = "SyntaxError";
+
+ if (typeof Error.captureStackTrace === "function") {
+ Error.captureStackTrace(this, peg$SyntaxError);
+ }
+ }
+
+ peg$subclass(peg$SyntaxError, Error);
+
+ function peg$parse(input) {
+ var options = arguments.length > 1 ? arguments[1] : {},
+ parser = this,
+
+ peg$FAILED = {},
+
+ peg$startRuleFunctions = { start: peg$parsestart },
+ peg$startRuleFunction = peg$parsestart,
+
+ peg$c0 = function(tree) {
+ return {
+ tree: tree.filter(function (o) {return o != null}),
+ functions: functions,
+ args: args,
+ variables: variables
+ }
+ },
+ peg$c1 = ",",
+ peg$c2 = { type: "literal", value: ",", description: "\",\"" },
+ peg$c3 = function(first, arg) {return arg},
+ peg$c4 = function(first, rest) {
+ return [first].concat(rest);
+ },
+ peg$c5 = "=",
+ peg$c6 = { type: "literal", value: "=", description: "\"=\"" },
+ peg$c7 = function(name, value) {
+ var arg = {
+ type: 'namedArg',
+ name: name,
+ value: value,
+ location: simpleLocation(location()),
+ text: text()
+ };
+ currentArgs.push(arg);
+ return arg;
+ },
+ peg$c8 = function(value) {
+ var exception = {
+ type: 'incompleteArgument',
+ currentArgs: currentArgs,
+ currentFunction: currentFunction,
+ location: simpleLocation(location()),
+ text: text()
+ }
+ error(JSON.stringify(exception));
+ },
+ peg$c9 = function(name) {
+ var exception = {
+ type: 'incompleteArgumentValue',
+ currentArgs: currentArgs,
+ currentFunction: currentFunction,
+ name: name,
+ location: simpleLocation(location()),
+ text: text()
+ }
+ error(JSON.stringify(exception));
+ },
+ peg$c10 = function(element) {return element},
+ peg$c11 = function(literal) {
+ var result = ltoo(literal);
+ result.location = simpleLocation(location()),
+ result.text = text();
+ return result;
+ },
+ peg$c12 = "$",
+ peg$c13 = { type: "literal", value: "$", description: "\"$\"" },
+ peg$c14 = function(name) {
+ if (variables[name]) {
+ return variables[name];
+ } else {
+ error('$' + name + ' is not defined')
+ }
+ },
+ peg$c15 = function(name, value) {
+ variables[name] = value;
+ },
+ peg$c16 = function(first, series) {return series},
+ peg$c17 = function(first, rest) {
+ return [first].concat(rest)
+ },
+ peg$c18 = /^[a-zA-Z]/,
+ peg$c19 = { type: "class", value: "[a-zA-Z]", description: "[a-zA-Z]" },
+ peg$c20 = /^[.a-zA-Z0-9_\-]/,
+ peg$c21 = { type: "class", value: "[.a-zA-Z0-9_-]", description: "[.a-zA-Z0-9_-]" },
+ peg$c22 = function(first, rest) {
+ currentFunction = first.join('') + rest.join('');
+ currentArgs = [];
+ return currentFunction;
+ },
+ peg$c23 = function(first, rest) { return first.join('') + rest.join('') },
+ peg$c24 = { type: "other", description: "function" },
+ peg$c25 = ".",
+ peg$c26 = { type: "literal", value: ".", description: "\".\"" },
+ peg$c27 = "(",
+ peg$c28 = { type: "literal", value: "(", description: "\"(\"" },
+ peg$c29 = ")",
+ peg$c30 = { type: "literal", value: ")", description: "\")\"" },
+ peg$c31 = function(name, arg_list) {
+ var result = {
+ type: 'function',
+ function: name,
+ arguments: arg_list || [],
+ location: simpleLocation(location()),
+ text: text()
+ }
+
+ result.arguments.forEach(function (arg) {
+ arg.function = name;
+ args.push(arg);
+ })
+
+ functions.push(result)
+ return result;
+ },
+ peg$c32 = function(func) {
+ var exception = {
+ type: 'incompleteFunction',
+ function: func,
+ location: simpleLocation(location()),
+ text: text()
+ }
+ error(JSON.stringify(exception));
+ },
+ peg$c33 = "@",
+ peg$c34 = { type: "literal", value: "@", description: "\"@\"" },
+ peg$c35 = ":",
+ peg$c36 = { type: "literal", value: ":", description: "\":\"" },
+ peg$c37 = function(plot, series) {
+ return {
+ type: 'reference',
+ plot: plot,
+ series: series
+ }
+ },
+ peg$c38 = function(plot) {
+ return {
+ type: 'reference',
+ plot: plot
+ }
+ },
+ peg$c39 = function(func, rest) {return {type: 'chain', chain: [func].concat(rest)}},
+ peg$c40 = function(grouped, functions) {
+ var first = {
+ type: 'chainList',
+ list: grouped
+ }
+ first.label = text();
+
+ return {type: "chain", chain: [first].concat(functions)};
+ },
+ peg$c41 = { type: "other", description: "literal" },
+ peg$c42 = "\"",
+ peg$c43 = { type: "literal", value: "\"", description: "\"\\\"\"" },
+ peg$c44 = function(chars) { return chars.join(''); },
+ peg$c45 = "'",
+ peg$c46 = { type: "literal", value: "'", description: "\"'\"" },
+ peg$c47 = "true",
+ peg$c48 = { type: "literal", value: "true", description: "\"true\"" },
+ peg$c49 = function() { return true; },
+ peg$c50 = "false",
+ peg$c51 = { type: "literal", value: "false", description: "\"false\"" },
+ peg$c52 = function() { return false; },
+ peg$c53 = "null",
+ peg$c54 = { type: "literal", value: "null", description: "\"null\"" },
+ peg$c55 = function() { return null; },
+ peg$c56 = /^[^()"',= \t]/,
+ peg$c57 = { type: "class", value: "[^()\"',=\\ \\t]", description: "[^()\"',=\\ \\t]" },
+ peg$c58 = function(string) { // this also matches numbers via Number()
+ var result = string.join('');
+ // Sort of hacky, but PEG doesn't have backtracking so
+ // a number rule is hard to read, and performs worse
+ if (isNaN(Number(result))) return result;
+ return Number(result)
+ },
+ peg$c59 = /^[ \t\r\n]/,
+ peg$c60 = { type: "class", value: "[\\ \\t\\r\\n]", description: "[\\ \\t\\r\\n]" },
+ peg$c61 = "\\",
+ peg$c62 = { type: "literal", value: "\\", description: "\"\\\\\"" },
+ peg$c63 = function(sequence) { return sequence; },
+ peg$c64 = /^[^"]/,
+ peg$c65 = { type: "class", value: "[^\"]", description: "[^\"]" },
+ peg$c66 = /^[^']/,
+ peg$c67 = { type: "class", value: "[^']", description: "[^']" },
+ peg$c68 = /^[0-9]/,
+ peg$c69 = { type: "class", value: "[0-9]", description: "[0-9]" },
+ peg$c70 = function(digits) {return parseInt(digits.join(''))},
+
+ peg$currPos = 0,
+ peg$savedPos = 0,
+ peg$posDetailsCache = [{ line: 1, column: 1, seenCR: false }],
+ peg$maxFailPos = 0,
+ peg$maxFailExpected = [],
+ peg$silentFails = 0,
+
+ peg$result;
+
+ if ("startRule" in options) {
+ if (!(options.startRule in peg$startRuleFunctions)) {
+ throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
+ }
+
+ peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
+ }
+
+ function text() {
+ return input.substring(peg$savedPos, peg$currPos);
+ }
+
+ function location() {
+ return peg$computeLocation(peg$savedPos, peg$currPos);
+ }
+
+ function expected(description) {
+ throw peg$buildException(
+ null,
+ [{ type: "other", description: description }],
+ input.substring(peg$savedPos, peg$currPos),
+ peg$computeLocation(peg$savedPos, peg$currPos)
+ );
+ }
+
+ function error(message) {
+ throw peg$buildException(
+ message,
+ null,
+ input.substring(peg$savedPos, peg$currPos),
+ peg$computeLocation(peg$savedPos, peg$currPos)
+ );
+ }
+
+ function peg$computePosDetails(pos) {
+ var details = peg$posDetailsCache[pos],
+ p, ch;
+
+ if (details) {
+ return details;
+ } else {
+ p = pos - 1;
+ while (!peg$posDetailsCache[p]) {
+ p--;
+ }
+
+ details = peg$posDetailsCache[p];
+ details = {
+ line: details.line,
+ column: details.column,
+ seenCR: details.seenCR
+ };
+
+ while (p < pos) {
+ ch = input.charAt(p);
+ if (ch === "\n") {
+ if (!details.seenCR) { details.line++; }
+ details.column = 1;
+ details.seenCR = false;
+ } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") {
+ details.line++;
+ details.column = 1;
+ details.seenCR = true;
+ } else {
+ details.column++;
+ details.seenCR = false;
+ }
+
+ p++;
+ }
+
+ peg$posDetailsCache[pos] = details;
+ return details;
+ }
+ }
+
+ function peg$computeLocation(startPos, endPos) {
+ var startPosDetails = peg$computePosDetails(startPos),
+ endPosDetails = peg$computePosDetails(endPos);
+
+ return {
+ start: {
+ offset: startPos,
+ line: startPosDetails.line,
+ column: startPosDetails.column
+ },
+ end: {
+ offset: endPos,
+ line: endPosDetails.line,
+ column: endPosDetails.column
+ }
+ };
+ }
+
+ function peg$fail(expected) {
+ if (peg$currPos < peg$maxFailPos) { return; }
+
+ if (peg$currPos > peg$maxFailPos) {
+ peg$maxFailPos = peg$currPos;
+ peg$maxFailExpected = [];
+ }
+
+ peg$maxFailExpected.push(expected);
+ }
+
+ function peg$buildException(message, expected, found, location) {
+ function cleanupExpected(expected) {
+ var i = 1;
+
+ expected.sort(function(a, b) {
+ if (a.description < b.description) {
+ return -1;
+ } else if (a.description > b.description) {
+ return 1;
+ } else {
+ return 0;
+ }
+ });
+
+ while (i < expected.length) {
+ if (expected[i - 1] === expected[i]) {
+ expected.splice(i, 1);
+ } else {
+ i++;
+ }
+ }
+ }
+
+ function buildMessage(expected, found) {
+ function stringEscape(s) {
+ function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); }
+
+ return s
+ .replace(/\\/g, '\\\\')
+ .replace(/"/g, '\\"')
+ .replace(/\x08/g, '\\b')
+ .replace(/\t/g, '\\t')
+ .replace(/\n/g, '\\n')
+ .replace(/\f/g, '\\f')
+ .replace(/\r/g, '\\r')
+ .replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
+ .replace(/[\x10-\x1F\x80-\xFF]/g, function(ch) { return '\\x' + hex(ch); })
+ .replace(/[\u0100-\u0FFF]/g, function(ch) { return '\\u0' + hex(ch); })
+ .replace(/[\u1000-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); });
+ }
+
+ var expectedDescs = new Array(expected.length),
+ expectedDesc, foundDesc, i;
+
+ for (i = 0; i < expected.length; i++) {
+ expectedDescs[i] = expected[i].description;
+ }
+
+ expectedDesc = expected.length > 1
+ ? expectedDescs.slice(0, -1).join(", ")
+ + " or "
+ + expectedDescs[expected.length - 1]
+ : expectedDescs[0];
+
+ foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input";
+
+ return "Expected " + expectedDesc + " but " + foundDesc + " found.";
+ }
+
+ if (expected !== null) {
+ cleanupExpected(expected);
+ }
+
+ return new peg$SyntaxError(
+ message !== null ? message : buildMessage(expected, found),
+ expected,
+ found,
+ location
+ );
+ }
+
+ function peg$parsestart() {
+ var s0, s1, s2;
+
+ s0 = peg$currPos;
+ s1 = peg$parsespace();
+ if (s1 === peg$FAILED) {
+ s1 = null;
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseseries();
+ if (s2 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c0(s2);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+ function peg$parsearg_list() {
+ var s0, s1, s2, s3, s4, s5, s6, s7;
+
+ s0 = peg$currPos;
+ s1 = peg$parseargument();
+ if (s1 !== peg$FAILED) {
+ s2 = [];
+ s3 = peg$currPos;
+ s4 = peg$parsespace();
+ if (s4 === peg$FAILED) {
+ s4 = null;
+ }
+ if (s4 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 44) {
+ s5 = peg$c1;
+ peg$currPos++;
+ } else {
+ s5 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c2); }
+ }
+ if (s5 !== peg$FAILED) {
+ s6 = peg$parsespace();
+ if (s6 === peg$FAILED) {
+ s6 = null;
+ }
+ if (s6 !== peg$FAILED) {
+ s7 = peg$parseargument();
+ if (s7 !== peg$FAILED) {
+ peg$savedPos = s3;
+ s4 = peg$c3(s1, s7);
+ s3 = s4;
+ } else {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ }
+ while (s3 !== peg$FAILED) {
+ s2.push(s3);
+ s3 = peg$currPos;
+ s4 = peg$parsespace();
+ if (s4 === peg$FAILED) {
+ s4 = null;
+ }
+ if (s4 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 44) {
+ s5 = peg$c1;
+ peg$currPos++;
+ } else {
+ s5 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c2); }
+ }
+ if (s5 !== peg$FAILED) {
+ s6 = peg$parsespace();
+ if (s6 === peg$FAILED) {
+ s6 = null;
+ }
+ if (s6 !== peg$FAILED) {
+ s7 = peg$parseargument();
+ if (s7 !== peg$FAILED) {
+ peg$savedPos = s3;
+ s4 = peg$c3(s1, s7);
+ s3 = s4;
+ } else {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ }
+ }
+ if (s2 !== peg$FAILED) {
+ s3 = peg$parsespace();
+ if (s3 === peg$FAILED) {
+ s3 = null;
+ }
+ if (s3 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 44) {
+ s4 = peg$c1;
+ peg$currPos++;
+ } else {
+ s4 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c2); }
+ }
+ if (s4 === peg$FAILED) {
+ s4 = null;
+ }
+ if (s4 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c4(s1, s2);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+ function peg$parseargument() {
+ var s0, s1, s2, s3, s4, s5;
+
+ s0 = peg$currPos;
+ s1 = peg$parseargument_name();
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parsespace();
+ if (s2 === peg$FAILED) {
+ s2 = null;
+ }
+ if (s2 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 61) {
+ s3 = peg$c5;
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c6); }
+ }
+ if (s3 !== peg$FAILED) {
+ s4 = peg$parsespace();
+ if (s4 === peg$FAILED) {
+ s4 = null;
+ }
+ if (s4 !== peg$FAILED) {
+ s5 = peg$parsearg_type();
+ if (s5 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c7(s1, s5);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ s1 = peg$parsespace();
+ if (s1 === peg$FAILED) {
+ s1 = null;
+ }
+ if (s1 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 61) {
+ s2 = peg$c5;
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c6); }
+ }
+ if (s2 !== peg$FAILED) {
+ s3 = peg$parsespace();
+ if (s3 === peg$FAILED) {
+ s3 = null;
+ }
+ if (s3 !== peg$FAILED) {
+ s4 = peg$parsearg_type();
+ if (s4 === peg$FAILED) {
+ s4 = null;
+ }
+ if (s4 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c8(s4);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ s1 = peg$parseargument_name();
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parsespace();
+ if (s2 === peg$FAILED) {
+ s2 = null;
+ }
+ if (s2 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 61) {
+ s3 = peg$c5;
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c6); }
+ }
+ if (s3 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c9(s1);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ s1 = peg$parsearg_type();
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c10(s1);
+ }
+ s0 = s1;
+ }
+ }
+ }
+
+ return s0;
+ }
+
+ function peg$parsearg_type() {
+ var s0, s1;
+
+ s0 = peg$parsevariable_get();
+ if (s0 === peg$FAILED) {
+ s0 = peg$parseseries_type();
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ s1 = peg$parseliteral();
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c11(s1);
+ }
+ s0 = s1;
+ }
+ }
+
+ return s0;
+ }
+
+ function peg$parsevariable_get() {
+ var s0, s1, s2;
+
+ s0 = peg$currPos;
+ if (input.charCodeAt(peg$currPos) === 36) {
+ s1 = peg$c12;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c13); }
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseargument_name();
+ if (s2 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c14(s2);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+ function peg$parsevariable_set() {
+ var s0, s1, s2, s3, s4, s5, s6;
+
+ s0 = peg$currPos;
+ if (input.charCodeAt(peg$currPos) === 36) {
+ s1 = peg$c12;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c13); }
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseargument_name();
+ if (s2 !== peg$FAILED) {
+ s3 = peg$parsespace();
+ if (s3 === peg$FAILED) {
+ s3 = null;
+ }
+ if (s3 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 61) {
+ s4 = peg$c5;
+ peg$currPos++;
+ } else {
+ s4 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c6); }
+ }
+ if (s4 !== peg$FAILED) {
+ s5 = peg$parsespace();
+ if (s5 === peg$FAILED) {
+ s5 = null;
+ }
+ if (s5 !== peg$FAILED) {
+ s6 = peg$parsearg_type();
+ if (s6 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c15(s2, s6);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+ function peg$parseseries_type() {
+ var s0;
+
+ s0 = peg$parsevariable_set();
+ if (s0 === peg$FAILED) {
+ s0 = peg$parsevariable_get();
+ if (s0 === peg$FAILED) {
+ s0 = peg$parsegroup();
+ if (s0 === peg$FAILED) {
+ s0 = peg$parsechain();
+ if (s0 === peg$FAILED) {
+ s0 = peg$parsereference();
+ }
+ }
+ }
+ }
+
+ return s0;
+ }
+
+ function peg$parseseries() {
+ var s0, s1, s2, s3, s4, s5, s6, s7;
+
+ s0 = peg$currPos;
+ s1 = peg$parseseries_type();
+ if (s1 !== peg$FAILED) {
+ s2 = [];
+ s3 = peg$currPos;
+ s4 = peg$parsespace();
+ if (s4 === peg$FAILED) {
+ s4 = null;
+ }
+ if (s4 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 44) {
+ s5 = peg$c1;
+ peg$currPos++;
+ } else {
+ s5 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c2); }
+ }
+ if (s5 !== peg$FAILED) {
+ s6 = peg$parsespace();
+ if (s6 === peg$FAILED) {
+ s6 = null;
+ }
+ if (s6 !== peg$FAILED) {
+ s7 = peg$parseseries_type();
+ if (s7 !== peg$FAILED) {
+ peg$savedPos = s3;
+ s4 = peg$c16(s1, s7);
+ s3 = s4;
+ } else {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ }
+ while (s3 !== peg$FAILED) {
+ s2.push(s3);
+ s3 = peg$currPos;
+ s4 = peg$parsespace();
+ if (s4 === peg$FAILED) {
+ s4 = null;
+ }
+ if (s4 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 44) {
+ s5 = peg$c1;
+ peg$currPos++;
+ } else {
+ s5 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c2); }
+ }
+ if (s5 !== peg$FAILED) {
+ s6 = peg$parsespace();
+ if (s6 === peg$FAILED) {
+ s6 = null;
+ }
+ if (s6 !== peg$FAILED) {
+ s7 = peg$parseseries_type();
+ if (s7 !== peg$FAILED) {
+ peg$savedPos = s3;
+ s4 = peg$c16(s1, s7);
+ s3 = s4;
+ } else {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ }
+ }
+ if (s2 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 44) {
+ s3 = peg$c1;
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c2); }
+ }
+ if (s3 === peg$FAILED) {
+ s3 = null;
+ }
+ if (s3 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c17(s1, s2);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+ function peg$parsefunction_name() {
+ var s0, s1, s2, s3;
+
+ s0 = peg$currPos;
+ s1 = [];
+ if (peg$c18.test(input.charAt(peg$currPos))) {
+ s2 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c19); }
+ }
+ if (s2 !== peg$FAILED) {
+ while (s2 !== peg$FAILED) {
+ s1.push(s2);
+ if (peg$c18.test(input.charAt(peg$currPos))) {
+ s2 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c19); }
+ }
+ }
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = [];
+ if (peg$c20.test(input.charAt(peg$currPos))) {
+ s3 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c21); }
+ }
+ while (s3 !== peg$FAILED) {
+ s2.push(s3);
+ if (peg$c20.test(input.charAt(peg$currPos))) {
+ s3 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c21); }
+ }
+ }
+ if (s2 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c22(s1, s2);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+ function peg$parseargument_name() {
+ var s0, s1, s2, s3;
+
+ s0 = peg$currPos;
+ s1 = [];
+ if (peg$c18.test(input.charAt(peg$currPos))) {
+ s2 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c19); }
+ }
+ if (s2 !== peg$FAILED) {
+ while (s2 !== peg$FAILED) {
+ s1.push(s2);
+ if (peg$c18.test(input.charAt(peg$currPos))) {
+ s2 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c19); }
+ }
+ }
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = [];
+ if (peg$c20.test(input.charAt(peg$currPos))) {
+ s3 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c21); }
+ }
+ while (s3 !== peg$FAILED) {
+ s2.push(s3);
+ if (peg$c20.test(input.charAt(peg$currPos))) {
+ s3 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c21); }
+ }
+ }
+ if (s2 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c23(s1, s2);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+ function peg$parsefunction() {
+ var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10;
+
+ peg$silentFails++;
+ s0 = peg$currPos;
+ s1 = peg$parsespace();
+ if (s1 === peg$FAILED) {
+ s1 = null;
+ }
+ if (s1 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 46) {
+ s2 = peg$c25;
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c26); }
+ }
+ if (s2 !== peg$FAILED) {
+ s3 = peg$parsefunction_name();
+ if (s3 !== peg$FAILED) {
+ s4 = peg$parsespace();
+ if (s4 === peg$FAILED) {
+ s4 = null;
+ }
+ if (s4 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 40) {
+ s5 = peg$c27;
+ peg$currPos++;
+ } else {
+ s5 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c28); }
+ }
+ if (s5 !== peg$FAILED) {
+ s6 = peg$parsespace();
+ if (s6 === peg$FAILED) {
+ s6 = null;
+ }
+ if (s6 !== peg$FAILED) {
+ s7 = peg$parsearg_list();
+ if (s7 === peg$FAILED) {
+ s7 = null;
+ }
+ if (s7 !== peg$FAILED) {
+ s8 = peg$parsespace();
+ if (s8 === peg$FAILED) {
+ s8 = null;
+ }
+ if (s8 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 41) {
+ s9 = peg$c29;
+ peg$currPos++;
+ } else {
+ s9 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c30); }
+ }
+ if (s9 !== peg$FAILED) {
+ s10 = peg$parsespace();
+ if (s10 === peg$FAILED) {
+ s10 = null;
+ }
+ if (s10 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c31(s3, s7);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ if (input.charCodeAt(peg$currPos) === 46) {
+ s1 = peg$c25;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c26); }
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parsefunction_name();
+ if (s2 === peg$FAILED) {
+ s2 = null;
+ }
+ if (s2 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c32(s2);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ }
+ peg$silentFails--;
+ if (s0 === peg$FAILED) {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c24); }
+ }
+
+ return s0;
+ }
+
+ function peg$parsereference() {
+ var s0, s1, s2, s3, s4;
+
+ s0 = peg$currPos;
+ if (input.charCodeAt(peg$currPos) === 64) {
+ s1 = peg$c33;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c34); }
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseinteger();
+ if (s2 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 58) {
+ s3 = peg$c35;
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c36); }
+ }
+ if (s3 !== peg$FAILED) {
+ s4 = peg$parseinteger();
+ if (s4 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c37(s2, s4);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ if (input.charCodeAt(peg$currPos) === 64) {
+ s1 = peg$c33;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c34); }
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parseinteger();
+ if (s2 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c38(s2);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ }
+
+ return s0;
+ }
+
+ function peg$parsechain() {
+ var s0, s1, s2, s3, s4;
+
+ s0 = peg$currPos;
+ s1 = peg$parsefunction();
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parsespace();
+ if (s2 === peg$FAILED) {
+ s2 = null;
+ }
+ if (s2 !== peg$FAILED) {
+ s3 = [];
+ s4 = peg$parsefunction();
+ while (s4 !== peg$FAILED) {
+ s3.push(s4);
+ s4 = peg$parsefunction();
+ }
+ if (s3 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c39(s1, s3);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+ function peg$parsegroup() {
+ var s0, s1, s2, s3, s4, s5, s6, s7;
+
+ s0 = peg$currPos;
+ if (input.charCodeAt(peg$currPos) === 40) {
+ s1 = peg$c27;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c28); }
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = peg$parsespace();
+ if (s2 === peg$FAILED) {
+ s2 = null;
+ }
+ if (s2 !== peg$FAILED) {
+ s3 = peg$parseseries();
+ if (s3 !== peg$FAILED) {
+ s4 = peg$parsespace();
+ if (s4 === peg$FAILED) {
+ s4 = null;
+ }
+ if (s4 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 41) {
+ s5 = peg$c29;
+ peg$currPos++;
+ } else {
+ s5 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c30); }
+ }
+ if (s5 !== peg$FAILED) {
+ s6 = [];
+ s7 = peg$parsefunction();
+ while (s7 !== peg$FAILED) {
+ s6.push(s7);
+ s7 = peg$parsefunction();
+ }
+ if (s6 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c40(s3, s6);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+ function peg$parseliteral() {
+ var s0, s1, s2, s3;
+
+ peg$silentFails++;
+ s0 = peg$currPos;
+ if (input.charCodeAt(peg$currPos) === 34) {
+ s1 = peg$c42;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c43); }
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = [];
+ s3 = peg$parsedq_char();
+ while (s3 !== peg$FAILED) {
+ s2.push(s3);
+ s3 = peg$parsedq_char();
+ }
+ if (s2 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 34) {
+ s3 = peg$c42;
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c43); }
+ }
+ if (s3 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c44(s2);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ if (input.charCodeAt(peg$currPos) === 39) {
+ s1 = peg$c45;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c46); }
+ }
+ if (s1 !== peg$FAILED) {
+ s2 = [];
+ s3 = peg$parsesq_char();
+ while (s3 !== peg$FAILED) {
+ s2.push(s3);
+ s3 = peg$parsesq_char();
+ }
+ if (s2 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 39) {
+ s3 = peg$c45;
+ peg$currPos++;
+ } else {
+ s3 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c46); }
+ }
+ if (s3 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c44(s2);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ if (input.substr(peg$currPos, 4) === peg$c47) {
+ s1 = peg$c47;
+ peg$currPos += 4;
+ } else {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c48); }
+ }
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c49();
+ }
+ s0 = s1;
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ if (input.substr(peg$currPos, 5) === peg$c50) {
+ s1 = peg$c50;
+ peg$currPos += 5;
+ } else {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c51); }
+ }
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c52();
+ }
+ s0 = s1;
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ if (input.substr(peg$currPos, 4) === peg$c53) {
+ s1 = peg$c53;
+ peg$currPos += 4;
+ } else {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c54); }
+ }
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c55();
+ }
+ s0 = s1;
+ if (s0 === peg$FAILED) {
+ s0 = peg$currPos;
+ s1 = [];
+ if (peg$c56.test(input.charAt(peg$currPos))) {
+ s2 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c57); }
+ }
+ if (s2 !== peg$FAILED) {
+ while (s2 !== peg$FAILED) {
+ s1.push(s2);
+ if (peg$c56.test(input.charAt(peg$currPos))) {
+ s2 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c57); }
+ }
+ }
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c58(s1);
+ }
+ s0 = s1;
+ }
+ }
+ }
+ }
+ }
+ peg$silentFails--;
+ if (s0 === peg$FAILED) {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c41); }
+ }
+
+ return s0;
+ }
+
+ function peg$parsespace() {
+ var s0, s1;
+
+ s0 = [];
+ if (peg$c59.test(input.charAt(peg$currPos))) {
+ s1 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c60); }
+ }
+ if (s1 !== peg$FAILED) {
+ while (s1 !== peg$FAILED) {
+ s0.push(s1);
+ if (peg$c59.test(input.charAt(peg$currPos))) {
+ s1 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c60); }
+ }
+ }
+ } else {
+ s0 = peg$FAILED;
+ }
+
+ return s0;
+ }
+
+ function peg$parsedq_char() {
+ var s0, s1, s2;
+
+ s0 = peg$currPos;
+ if (input.charCodeAt(peg$currPos) === 92) {
+ s1 = peg$c61;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c62); }
+ }
+ if (s1 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 34) {
+ s2 = peg$c42;
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c43); }
+ }
+ if (s2 === peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 92) {
+ s2 = peg$c61;
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c62); }
+ }
+ }
+ if (s2 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c63(s2);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ if (peg$c64.test(input.charAt(peg$currPos))) {
+ s0 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s0 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c65); }
+ }
+ }
+
+ return s0;
+ }
+
+ function peg$parsesq_char() {
+ var s0, s1, s2;
+
+ s0 = peg$currPos;
+ if (input.charCodeAt(peg$currPos) === 92) {
+ s1 = peg$c61;
+ peg$currPos++;
+ } else {
+ s1 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c62); }
+ }
+ if (s1 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 39) {
+ s2 = peg$c45;
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c46); }
+ }
+ if (s2 === peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 92) {
+ s2 = peg$c61;
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c62); }
+ }
+ }
+ if (s2 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c63(s2);
+ s0 = s1;
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
+ }
+ if (s0 === peg$FAILED) {
+ if (peg$c66.test(input.charAt(peg$currPos))) {
+ s0 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s0 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c67); }
+ }
+ }
+
+ return s0;
+ }
+
+ function peg$parseinteger() {
+ var s0, s1, s2;
+
+ s0 = peg$currPos;
+ s1 = [];
+ if (peg$c68.test(input.charAt(peg$currPos))) {
+ s2 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c69); }
+ }
+ if (s2 !== peg$FAILED) {
+ while (s2 !== peg$FAILED) {
+ s1.push(s2);
+ if (peg$c68.test(input.charAt(peg$currPos))) {
+ s2 = input.charAt(peg$currPos);
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$c69); }
+ }
+ }
+ } else {
+ s1 = peg$FAILED;
+ }
+ if (s1 !== peg$FAILED) {
+ peg$savedPos = s0;
+ s1 = peg$c70(s1);
+ }
+ s0 = s1;
+
+ return s0;
+ }
+
+
+ function ltoo (literal) {
+ return {type: 'literal', value: literal}
+ }
+
+ function simpleLocation (location) {
+ // Returns an object representing the position of the function within the expression,
+ // demarcated by the position of its first character and last character. We calculate these values
+ // using the offset because the expression could span multiple lines, and we don't want to deal
+ // with column and line values.
+ return {
+ min: location.start.offset,
+ max: location.end.offset
+ }
+ }
+
+ var currentFunction;
+ var currentArgs = [];
+
+ var functions = [];
+ var args = [];
+ var variables = {};
+
+
+
+ peg$result = peg$startRuleFunction();
+
+ if (peg$result !== peg$FAILED && peg$currPos === input.length) {
+ return peg$result;
+ } else {
+ if (peg$result !== peg$FAILED && peg$currPos < input.length) {
+ peg$fail({ type: "end", description: "end of input" });
+ }
+
+ throw peg$buildException(
+ null,
+ peg$maxFailExpected,
+ peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null,
+ peg$maxFailPos < input.length
+ ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1)
+ : peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
+ );
+ }
+ }
+
+ return {
+ SyntaxError: peg$SyntaxError,
+ parse: peg$parse
+ };
+})();
\ No newline at end of file
diff --git a/src/legacy/core_plugins/timelion/public/directives/__tests__/timelion_expression_input_helpers.js b/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.test.ts
similarity index 70%
rename from src/legacy/core_plugins/timelion/public/directives/__tests__/timelion_expression_input_helpers.js
rename to src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.test.ts
index ea2d44bcaefe0..18a0c0872dc03 100644
--- a/src/legacy/core_plugins/timelion/public/directives/__tests__/timelion_expression_input_helpers.js
+++ b/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.test.ts
@@ -17,19 +17,16 @@
* under the License.
*/
-import expect from '@kbn/expect';
-import PEG from 'pegjs';
-import grammar from 'raw-loader!../../../../vis_type_timelion/public/chain.peg';
-import { SUGGESTION_TYPE, suggest } from '../timelion_expression_input_helpers';
-import { getArgValueSuggestions } from '../../../../vis_type_timelion/public/helpers/arg_value_suggestions';
-import {
- setIndexPatterns,
- setSavedObjectsClient,
-} from '../../../../vis_type_timelion/public/helpers/plugin_services';
+import { SUGGESTION_TYPE, suggest } from './timelion_expression_input_helpers';
+import { getArgValueSuggestions } from '../helpers/arg_value_suggestions';
+import { setIndexPatterns, setSavedObjectsClient } from '../helpers/plugin_services';
+import { IndexPatterns } from 'src/plugins/data/public';
+import { SavedObjectsClient } from 'kibana/public';
+import { ITimelionFunction } from '../../common/types';
describe('Timelion expression suggestions', () => {
- setIndexPatterns({});
- setSavedObjectsClient({});
+ setIndexPatterns({} as IndexPatterns);
+ setSavedObjectsClient({} as SavedObjectsClient);
const argValueSuggestions = getArgValueSuggestions();
@@ -45,17 +42,13 @@ describe('Timelion expression suggestions', () => {
suggestions: [{ name: 'value1' }],
},
],
- };
+ } as ITimelionFunction;
const myFunc2 = {
name: 'myFunc2',
chainable: false,
args: [{ name: 'argA' }, { name: 'argAB' }, { name: 'argABC' }],
- };
+ } as ITimelionFunction;
const functionList = [func1, myFunc2];
- let Parser;
- beforeEach(function() {
- Parser = PEG.generate(grammar);
- });
describe('parse exception', () => {
describe('incompleteFunction', () => {
@@ -65,17 +58,12 @@ describe('Timelion expression suggestions', () => {
const suggestions = await suggest(
expression,
functionList,
- Parser,
cursorPosition,
argValueSuggestions
);
- expect(suggestions).to.eql({
+ expect(suggestions).toEqual({
list: [func1, myFunc2],
- location: {
- min: 0,
- max: 1,
- },
- type: 'functions',
+ type: SUGGESTION_TYPE.FUNCTIONS,
});
});
it('should filter function suggestions by function name', async () => {
@@ -84,17 +72,12 @@ describe('Timelion expression suggestions', () => {
const suggestions = await suggest(
expression,
functionList,
- Parser,
cursorPosition,
argValueSuggestions
);
- expect(suggestions).to.eql({
+ expect(suggestions).toEqual({
list: [myFunc2],
- location: {
- min: 0,
- max: 4,
- },
- type: 'functions',
+ type: SUGGESTION_TYPE.FUNCTIONS,
});
});
});
@@ -106,17 +89,12 @@ describe('Timelion expression suggestions', () => {
const suggestions = await suggest(
expression,
functionList,
- Parser,
cursorPosition,
argValueSuggestions
);
- expect(suggestions).to.eql({
+ expect(suggestions).toEqual({
list: [],
- location: {
- min: 11,
- max: 12,
- },
- type: 'arguments',
+ type: SUGGESTION_TYPE.ARGUMENTS,
});
});
@@ -126,17 +104,12 @@ describe('Timelion expression suggestions', () => {
const suggestions = await suggest(
expression,
functionList,
- Parser,
cursorPosition,
argValueSuggestions
);
- expect(suggestions).to.eql({
+ expect(suggestions).toEqual({
list: myFunc2.args,
- location: {
- min: 9,
- max: 10,
- },
- type: 'arguments',
+ type: SUGGESTION_TYPE.ARGUMENTS,
});
});
@@ -146,17 +119,12 @@ describe('Timelion expression suggestions', () => {
const suggestions = await suggest(
expression,
functionList,
- Parser,
cursorPosition,
argValueSuggestions
);
- expect(suggestions).to.eql({
+ expect(suggestions).toEqual({
list: myFunc2.args,
- location: {
- min: 9,
- max: 25,
- },
- type: 'arguments',
+ type: SUGGESTION_TYPE.ARGUMENTS,
});
});
@@ -166,17 +134,12 @@ describe('Timelion expression suggestions', () => {
const suggestions = await suggest(
expression,
functionList,
- Parser,
cursorPosition,
argValueSuggestions
);
- expect(suggestions).to.eql({
+ expect(suggestions).toEqual({
list: [{ name: 'argA' }, { name: 'argAB', suggestions: [{ name: 'value1' }] }],
- location: {
- min: 7,
- max: 8,
- },
- type: 'arguments',
+ type: SUGGESTION_TYPE.ARGUMENTS,
});
});
@@ -186,17 +149,12 @@ describe('Timelion expression suggestions', () => {
const suggestions = await suggest(
expression,
functionList,
- Parser,
cursorPosition,
argValueSuggestions
);
- expect(suggestions).to.eql({
+ expect(suggestions).toEqual({
list: [{ name: 'argA' }, { name: 'argABC' }],
- location: {
- min: 24,
- max: 25,
- },
- type: 'arguments',
+ type: SUGGESTION_TYPE.ARGUMENTS,
});
});
});
@@ -208,17 +166,12 @@ describe('Timelion expression suggestions', () => {
const suggestions = await suggest(
expression,
functionList,
- Parser,
cursorPosition,
argValueSuggestions
);
- expect(suggestions).to.eql({
+ expect(suggestions).toEqual({
list: [],
- location: {
- min: 11,
- max: 11,
- },
- type: 'argument_value',
+ type: SUGGESTION_TYPE.ARGUMENT_VALUE,
});
});
@@ -228,17 +181,12 @@ describe('Timelion expression suggestions', () => {
const suggestions = await suggest(
expression,
functionList,
- Parser,
cursorPosition,
argValueSuggestions
);
- expect(suggestions).to.eql({
+ expect(suggestions).toEqual({
list: [{ name: 'value1' }],
- location: {
- min: 11,
- max: 11,
- },
- type: 'argument_value',
+ type: SUGGESTION_TYPE.ARGUMENT_VALUE,
});
});
});
@@ -252,17 +200,12 @@ describe('Timelion expression suggestions', () => {
const suggestions = await suggest(
expression,
functionList,
- Parser,
cursorPosition,
argValueSuggestions
);
- expect(suggestions).to.eql({
+ expect(suggestions).toEqual({
list: [func1],
- location: {
- min: 0,
- max: 8,
- },
- type: 'functions',
+ type: SUGGESTION_TYPE.FUNCTIONS,
});
});
});
@@ -275,17 +218,12 @@ describe('Timelion expression suggestions', () => {
const suggestions = await suggest(
expression,
functionList,
- Parser,
cursorPosition,
argValueSuggestions
);
- expect(suggestions).to.eql({
+ expect(suggestions).toEqual({
list: myFunc2.args,
- location: {
- min: 9,
- max: 9,
- },
- type: 'arguments',
+ type: SUGGESTION_TYPE.ARGUMENTS,
});
});
it('should not provide argument suggestions for argument that is all ready set in function def', async () => {
@@ -294,18 +232,12 @@ describe('Timelion expression suggestions', () => {
const suggestions = await suggest(
expression,
functionList,
- Parser,
cursorPosition,
argValueSuggestions
);
- expect(suggestions.type).to.equal(SUGGESTION_TYPE.ARGUMENTS);
- expect(suggestions).to.eql({
+ expect(suggestions).toEqual({
list: [{ name: 'argA' }, { name: 'argABC' }],
- location: {
- min: 24,
- max: 24,
- },
- type: 'arguments',
+ type: SUGGESTION_TYPE.ARGUMENTS,
});
});
it('should filter argument suggestions by argument name', async () => {
@@ -314,17 +246,12 @@ describe('Timelion expression suggestions', () => {
const suggestions = await suggest(
expression,
functionList,
- Parser,
cursorPosition,
argValueSuggestions
);
- expect(suggestions).to.eql({
+ expect(suggestions).toEqual({
list: [{ name: 'argAB' }, { name: 'argABC' }],
- location: {
- min: 9,
- max: 14,
- },
- type: 'arguments',
+ type: SUGGESTION_TYPE.ARGUMENTS,
});
});
it('should not show first argument for chainable functions', async () => {
@@ -333,17 +260,12 @@ describe('Timelion expression suggestions', () => {
const suggestions = await suggest(
expression,
functionList,
- Parser,
cursorPosition,
argValueSuggestions
);
- expect(suggestions).to.eql({
+ expect(suggestions).toEqual({
list: [{ name: 'argA' }, { name: 'argAB', suggestions: [{ name: 'value1' }] }],
- location: {
- min: 7,
- max: 7,
- },
- type: 'arguments',
+ type: SUGGESTION_TYPE.ARGUMENTS,
});
});
});
@@ -354,17 +276,12 @@ describe('Timelion expression suggestions', () => {
const suggestions = await suggest(
expression,
functionList,
- Parser,
cursorPosition,
argValueSuggestions
);
- expect(suggestions).to.eql({
+ expect(suggestions).toEqual({
list: [],
- location: {
- min: 14,
- max: 16,
- },
- type: 'argument_value',
+ type: SUGGESTION_TYPE.ARGUMENT_VALUE,
});
});
@@ -374,17 +291,12 @@ describe('Timelion expression suggestions', () => {
const suggestions = await suggest(
expression,
functionList,
- Parser,
cursorPosition,
argValueSuggestions
);
- expect(suggestions).to.eql({
+ expect(suggestions).toEqual({
list: [{ name: 'value1' }],
- location: {
- min: 13,
- max: 16,
- },
- type: 'argument_value',
+ type: SUGGESTION_TYPE.ARGUMENT_VALUE,
});
});
});
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts b/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts
index 5aa05fb16466b..d7818680c9543 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts
+++ b/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts
@@ -18,18 +18,17 @@
*/
import { get, startsWith } from 'lodash';
-import PEG from 'pegjs';
import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api';
+import { i18n } from '@kbn/i18n';
+import { Parser } from 'pegjs';
+
// @ts-ignore
-import grammar from 'raw-loader!../chain.peg';
+import { parse } from '../_generated_/chain';
-import { i18n } from '@kbn/i18n';
import { ITimelionFunction, TimelionFunctionArgs } from '../../common/types';
import { ArgValueSuggestions, FunctionArg, Location } from '../helpers/arg_value_suggestions';
-const Parser = PEG.generate(grammar);
-
export enum SUGGESTION_TYPE {
ARGUMENTS = 'arguments',
ARGUMENT_VALUE = 'argument_value',
@@ -57,7 +56,7 @@ function getArgumentsHelp(
}
async function extractSuggestionsFromParsedResult(
- result: ReturnType,
+ result: ReturnType,
cursorPosition: number,
functionList: ITimelionFunction[],
argValueSuggestions: ArgValueSuggestions
@@ -141,7 +140,7 @@ export async function suggest(
argValueSuggestions: ArgValueSuggestions
) {
try {
- const result = await Parser.parse(expression);
+ const result = await parse(expression);
return await extractSuggestionsFromParsedResult(
result,
diff --git a/tasks/config/peg.js b/tasks/config/peg.js
index 0c57c6139d298..1d8667840faba 100644
--- a/tasks/config/peg.js
+++ b/tasks/config/peg.js
@@ -25,4 +25,8 @@ module.exports = {
allowedStartRules: ['start', 'Literal'],
},
},
+ timelion_chain: {
+ src: 'src/legacy/core_plugins/vis_type_timelion/public/chain.peg',
+ dest: 'src/legacy/core_plugins/vis_type_timelion/public/_generated_/chain.js',
+ },
};
From be31198d21c1693c6d67a4cc47726325ca192a45 Mon Sep 17 00:00:00 2001
From: Aleh Zasypkin
Date: Tue, 21 Jan 2020 15:55:54 +0100
Subject: [PATCH 010/187] Re-enable Kerberos + anonymous access test. (#55377)
---
.../kerberos_api_integration/apis/security/kerberos_login.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/x-pack/test/kerberos_api_integration/apis/security/kerberos_login.ts b/x-pack/test/kerberos_api_integration/apis/security/kerberos_login.ts
index bd35f21d8f428..0346da334d2f2 100644
--- a/x-pack/test/kerberos_api_integration/apis/security/kerberos_login.ts
+++ b/x-pack/test/kerberos_api_integration/apis/security/kerberos_login.ts
@@ -344,8 +344,7 @@ export default function({ getService }: FtrProviderContext) {
});
});
- // FAILING: https://github.com/elastic/kibana/issues/52969
- describe.skip('API access with missing access token document or expired refresh token.', () => {
+ describe('API access with missing access token document or expired refresh token.', () => {
let sessionCookie: Cookie;
beforeEach(async () => {
From ce286f543e4bd4d72ec7ffefac3a410cac2eaf0f Mon Sep 17 00:00:00 2001
From: James Gowdy
Date: Tue, 21 Jan 2020 15:25:58 +0000
Subject: [PATCH 011/187] [ML] Adding missing job groups to recognizer wizard
(#55392)
---
.../application/jobs/new_job/recognize/page.tsx | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/recognize/page.tsx b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/recognize/page.tsx
index 141ed5d1bbb8f..c4a96d9e373c8 100644
--- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/recognize/page.tsx
+++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/recognize/page.tsx
@@ -77,6 +77,7 @@ export const Page: FC = ({ moduleId, existingGroupIds }) => {
const [kibanaObjects, setKibanaObjects] = useState({});
const [saveState, setSaveState] = useState(SAVE_STATE.NOT_SAVED);
const [resultsUrl, setResultsUrl] = useState('');
+ const [existingGroups, setExistingGroups] = useState(existingGroupIds);
// #endregion
const {
@@ -109,6 +110,10 @@ export const Page: FC = ({ moduleId, existingGroupIds }) => {
setKibanaObjects(kibanaObjectsResult);
setSaveState(SAVE_STATE.NOT_SAVED);
+
+ // mix existing groups from the server with the groups used across all jobs in the module.
+ const moduleGroups = [...response.jobs.map(j => j.config.groups || [])].flat();
+ setExistingGroups([...new Set([...existingGroups, ...moduleGroups])]);
} catch (e) {
// eslint-disable-next-line no-console
console.error(e);
@@ -222,6 +227,12 @@ export const Page: FC = ({ moduleId, existingGroupIds }) => {
...jobOverrides,
[job.job_id as string]: job,
});
+ if (job.groups !== undefined) {
+ // add newly added jobs to the list of existing groups
+ // for use when editing other jobs in the module
+ const groups = [...new Set([...existingGroups, ...job.groups])];
+ setExistingGroups(groups);
+ }
};
const isFormVisible = [SAVE_STATE.NOT_SAVED, SAVE_STATE.SAVING].includes(saveState);
@@ -304,7 +315,7 @@ export const Page: FC = ({ moduleId, existingGroupIds }) => {
jobs={jobs}
jobPrefix={jobPrefix}
saveState={saveState}
- existingGroupIds={existingGroupIds}
+ existingGroupIds={existingGroups}
jobOverrides={jobOverrides}
onJobOverridesChange={onJobOverridesChange}
/>
From 2bf111c50fc211454a6f658b488323e22a8ad4e3 Mon Sep 17 00:00:00 2001
From: Justin Kambic
Date: Tue, 21 Jan 2020 10:58:57 -0500
Subject: [PATCH 012/187] [Uptime] Fix flaky uptime overview page test (#54767)
* Fix flaky uptime overview page test.
* Increase timeout for url checks.
* Prefer standard `retry.try` to custom retry implementation.
* Remove unneeded symbol.
* Remove unnecessary type annotation.
Co-authored-by: Elastic Machine
---
x-pack/test/functional/apps/uptime/overview.ts | 9 +++------
x-pack/test/functional/page_objects/uptime_page.ts | 8 ++++++--
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/x-pack/test/functional/apps/uptime/overview.ts b/x-pack/test/functional/apps/uptime/overview.ts
index ba701da6e517d..2ef6a381a6a30 100644
--- a/x-pack/test/functional/apps/uptime/overview.ts
+++ b/x-pack/test/functional/apps/uptime/overview.ts
@@ -50,13 +50,11 @@ export default ({ getPageObjects }: FtrProviderContext) => {
]);
});
- // flakey see https://github.com/elastic/kibana/issues/54527
- it.skip('pagination is cleared when filter criteria changes', async () => {
+ it('pagination is cleared when filter criteria changes', async () => {
await pageObjects.uptime.goToUptimePageAndSetDateRange(DEFAULT_DATE_START, DEFAULT_DATE_END);
await pageObjects.uptime.changePage('next');
// there should now be pagination data in the URL
- const contains = await pageObjects.uptime.pageUrlContains('pagination');
- expect(contains).to.be(true);
+ await pageObjects.uptime.pageUrlContains('pagination');
await pageObjects.uptime.pageHasExpectedIds([
'0010-down',
'0011-up',
@@ -71,8 +69,7 @@ export default ({ getPageObjects }: FtrProviderContext) => {
]);
await pageObjects.uptime.setStatusFilter('up');
// ensure that pagination is removed from the URL
- const doesNotContain = await pageObjects.uptime.pageUrlContains('pagination');
- expect(doesNotContain).to.be(false);
+ await pageObjects.uptime.pageUrlContains('pagination', false);
await pageObjects.uptime.pageHasExpectedIds([
'0000-intermittent',
'0001-up',
diff --git a/x-pack/test/functional/page_objects/uptime_page.ts b/x-pack/test/functional/page_objects/uptime_page.ts
index f04f96148583f..2ae0ea38c957b 100644
--- a/x-pack/test/functional/page_objects/uptime_page.ts
+++ b/x-pack/test/functional/page_objects/uptime_page.ts
@@ -4,11 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import expect from '@kbn/expect';
import { FtrProviderContext } from '../ftr_provider_context';
export function UptimePageProvider({ getPageObjects, getService }: FtrProviderContext) {
const pageObjects = getPageObjects(['common', 'timePicker']);
const uptimeService = getService('uptime');
+ const retry = getService('retry');
return new (class UptimePage {
public async goToUptimePageAndSetDateRange(
@@ -51,8 +53,10 @@ export function UptimePageProvider({ getPageObjects, getService }: FtrProviderCo
await Promise.all(monitorIdsToCheck.map(id => uptimeService.monitorPageLinkExists(id)));
}
- public async pageUrlContains(value: string) {
- return await uptimeService.urlContains(value);
+ public async pageUrlContains(value: string, expected: boolean = true) {
+ retry.try(async () => {
+ expect(await uptimeService.urlContains(value)).to.eql(expected);
+ });
}
public async changePage(direction: 'next' | 'prev') {
From b0af1bf95c26a03135a382c0c758b54782e514fa Mon Sep 17 00:00:00 2001
From: Josh Dover
Date: Tue, 21 Jan 2020 09:19:08 -0700
Subject: [PATCH 013/187] Clear core loading indicator just before UI is
rendered (#55242)
Co-authored-by: Elastic Machine
---
src/core/public/core_system.test.ts | 7 +++----
src/core/public/core_system.ts | 15 ++++++++-------
2 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/src/core/public/core_system.test.ts b/src/core/public/core_system.test.ts
index 1ee41fe64418e..94fa74f4bd861 100644
--- a/src/core/public/core_system.test.ts
+++ b/src/core/public/core_system.test.ts
@@ -429,15 +429,14 @@ describe('Notifications targetDomElement', () => {
rootDomElement,
});
- let targetDomElementParentInStart: HTMLElement | null;
+ let targetDomElementInStart: HTMLElement | null;
MockNotificationsService.start.mockImplementation(({ targetDomElement }): any => {
- expect(targetDomElement.parentElement).not.toBeNull();
- targetDomElementParentInStart = targetDomElement.parentElement;
+ targetDomElementInStart = targetDomElement;
});
// Starting the core system should pass the targetDomElement as a child of the rootDomElement
await core.setup();
await core.start();
- expect(targetDomElementParentInStart!).toBe(rootDomElement);
+ expect(targetDomElementInStart!.parentElement).toBe(rootDomElement);
});
});
diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts
index 97bd49416f705..be69cacdd271a 100644
--- a/src/core/public/core_system.ts
+++ b/src/core/public/core_system.ts
@@ -222,13 +222,6 @@ export class CoreSystem {
const notificationsTargetDomElement = document.createElement('div');
const overlayTargetDomElement = document.createElement('div');
- // ensure the rootDomElement is empty
- this.rootDomElement.textContent = '';
- this.rootDomElement.classList.add('coreSystemRootDomElement');
- this.rootDomElement.appendChild(coreUiTargetDomElement);
- this.rootDomElement.appendChild(notificationsTargetDomElement);
- this.rootDomElement.appendChild(overlayTargetDomElement);
-
const overlays = this.overlay.start({
i18n,
targetDomElement: overlayTargetDomElement,
@@ -276,6 +269,14 @@ export class CoreSystem {
};
const plugins = await this.plugins.start(core);
+
+ // ensure the rootDomElement is empty
+ this.rootDomElement.textContent = '';
+ this.rootDomElement.classList.add('coreSystemRootDomElement');
+ this.rootDomElement.appendChild(coreUiTargetDomElement);
+ this.rootDomElement.appendChild(notificationsTargetDomElement);
+ this.rootDomElement.appendChild(overlayTargetDomElement);
+
const rendering = this.rendering.start({
application,
chrome,
From ce2930ec5154ef4ccba8420744a522225cd24210 Mon Sep 17 00:00:00 2001
From: Nick Partridge
Date: Tue, 21 Jan 2020 10:46:56 -0600
Subject: [PATCH 014/187] De-angularize vis tooltips (#54954)
* Remove angular dependencey from vis/tooltip
* Move tooltip logic into vislib
* Remove and fix all ngMock refs in vislib tests
* Add numeral to renovate config
* Add vis_type_vislib to codeowners
* Move vis_legend into vislib and fix errors
* vis_type_vislib/public imports to be only top-level
---
.github/CODEOWNERS | 1 +
.i18nrc.json | 4 +-
package.json | 1 +
renovate.json5 | 8 +
.../public/components/region_map_options.tsx | 6 +-
.../public/components/tile_map_options.tsx | 2 +-
.../components/wms_internal_options.tsx | 2 +-
.../public/components/wms_options.tsx | 2 +-
.../public/settings_options.tsx | 2 +-
.../public/components/metric_vis_options.tsx | 4 +-
.../vis_type_metric/public/metric_vis_fn.ts | 2 +-
.../vis_type_metric/public/metric_vis_type.ts | 2 +-
.../vis_type_metric/public/types.ts | 3 +-
.../public/components/table_vis_options.tsx | 6 +-
.../public/components/tag_cloud_options.tsx | 2 +-
.../vis_type_vislib/public/area.ts | 14 +-
.../components/common/basic_options.tsx | 4 +-
.../public/components/common/color_ranges.tsx | 2 +-
.../public/components/common/color_schema.tsx | 8 +-
.../public/components/common/range.tsx | 2 +-
.../components/common/truncate_labels.tsx | 2 +-
.../components/options/gauge/labels_panel.tsx | 10 +-
.../components/options/gauge/ranges_panel.tsx | 14 +-
.../components/options/gauge/style_panel.tsx | 6 +-
.../components/options/heatmap/index.tsx | 23 +-
.../options/heatmap/labels_panel.tsx | 10 +-
.../category_axis_panel.test.tsx.snap | 2 +-
.../__snapshots__/label_options.test.tsx.snap | 2 +-
.../value_axes_panel.test.tsx.snap | 2 +-
.../metrics_axes/category_axis_panel.tsx | 6 +-
.../options/metrics_axes/chart_options.tsx | 10 +-
.../metrics_axes/custom_extents_options.tsx | 15 +-
.../options/metrics_axes/label_options.tsx | 29 +-
.../options/metrics_axes/line_options.tsx | 8 +-
.../options/metrics_axes/series_panel.tsx | 4 +-
.../options/metrics_axes/value_axes_panel.tsx | 8 +-
.../metrics_axes/value_axis_options.tsx | 14 +-
.../options/metrics_axes/y_extents.tsx | 20 +-
.../public/components/options/pie.tsx | 12 +-
.../options/point_series/grid_panel.tsx | 10 +-
.../options/point_series/point_series.tsx | 8 +-
.../options/point_series/threshold_panel.tsx | 40 +-
.../vis_type_vislib/public/gauge.ts | 8 +-
.../vis_type_vislib/public/goal.ts | 8 +-
.../vis_type_vislib/public/heatmap.ts | 12 +-
.../vis_type_vislib/public/histogram.ts | 14 +-
.../vis_type_vislib/public/horizontal_bar.ts | 14 +-
.../vis_type_vislib/public/index.ts | 14 +
.../vis_type_vislib/public/legacy.ts | 14 -
.../vis_type_vislib/public/legacy_imports.ts | 5 +-
.../vis_type_vislib/public/line.ts | 14 +-
.../vis_type_vislib/public/pie.ts | 10 +-
.../vis_type_vislib/public/pie_fn.ts | 2 +-
.../vis_type_vislib/public/plugin.ts | 4 -
.../public/utils/collections.ts | 60 +--
.../public/utils/common_config.tsx | 6 +-
.../vis_type_vislib/public/vis_controller.tsx | 6 +-
.../public/vis_type_vislib_vis_fn.ts | 2 +-
.../components}/_tooltip_formatter.js | 13 +-
.../__tests__/components/heatmap_color.js | 3 -
.../vislib/__tests__/components/labels.js | 74 ++--
.../__tests__/components}/positioning.js | 3 +-
.../public/vislib/__tests__/index.js | 11 +-
.../public/vislib/__tests__/lib/axis/axis.js | 60 ++-
.../public/vislib/__tests__/lib/axis_title.js | 102 +++--
.../vislib/__tests__/lib/chart_title.js | 54 ++-
.../public/vislib/__tests__/lib/data.js | 28 +-
.../public/vislib/__tests__/lib/dispatch.js | 90 ++---
.../vislib/__tests__/lib/error_handler.js | 11 +-
.../__tests__/lib/fixtures/_vis_fixture.js | 51 ++-
.../vislib/__tests__/lib/handler/handler.js | 27 +-
.../vislib/__tests__/lib/layout/layout.js | 28 +-
.../__tests__/lib/layout/layout_types.js | 11 +-
.../lib/layout/splits/column_chart/splits.js | 103 +++--
.../lib/layout/splits/gauge_chart/splits.js | 43 +-
.../lib/layout/types/column_layout.js | 19 +-
.../__tests__/lib/types/point_series.js | 7 +-
.../public/vislib/__tests__/lib/vis_config.js | 40 +-
.../public/vislib/__tests__/lib/x_axis.js | 54 ++-
.../public/vislib/__tests__/lib/y_axis.js | 23 +-
.../public/vislib/__tests__/vis.js | 38 +-
.../__tests__/visualizations/area_chart.js | 73 ++--
.../vislib/__tests__/visualizations/chart.js | 42 +-
.../__tests__/visualizations/column_chart.js | 86 ++--
.../__tests__/visualizations/gauge_chart.js | 24 +-
.../__tests__/visualizations/heatmap_chart.js | 31 +-
.../__tests__/visualizations/line_chart.js | 70 ++--
.../__tests__/visualizations/pie_chart.js | 90 ++---
.../__tests__/visualizations/time_marker.js | 41 +-
.../__tests__/visualizations/vis_types.js | 13 +-
.../vis_type_vislib/public/vislib/_index.scss | 3 +
.../public/vislib}/_vislib_vis_type.scss | 3 +
.../__snapshots__/legend.test.tsx.snap} | 0
.../vislib/components/legend/_index.scss | 1 +
.../vislib/components/legend/_legend.scss} | 9 +-
.../public/vislib/components/legend}/index.ts | 2 +-
.../vislib/components/legend/legend.test.tsx} | 6 +-
.../vislib/components/legend/legend.tsx} | 12 +-
.../vislib/components/legend/legend_item.tsx} | 10 +-
.../vislib/components/legend}/models.ts | 0
.../vislib/components/legend}/pie_utils.ts | 0
.../components/tooltip/_collect_branch.js | 0
.../tooltip/_collect_branch.test.js | 0
.../_hierarchical_tooltip_formatter.js | 93 +++++
.../vislib}/components/tooltip/_index.scss | 0
.../tooltip/_pointseries_tooltip_formatter.js | 79 ++++
.../vislib}/components/tooltip/_tooltip.scss | 3 +-
.../vislib}/components/tooltip/index.js | 4 +-
.../components/tooltip/position_tooltip.js | 0
.../vislib}/components/tooltip/tooltip.js | 11 +-
.../public/vislib/lib/chart_title.js | 2 +-
.../public/vislib/lib/types/point_series.js | 2 +-
.../public/vislib/visualizations/_chart.js | 11 +-
.../vislib/visualizations/point_series.js | 18 +-
src/legacy/ui/public/vis/_index.scss | 2 -
.../ui/public/vis/components/_index.scss | 1 -
.../tooltip/_hierarchical_tooltip.html | 28 --
.../_hierarchical_tooltip_formatter.js | 82 ----
.../tooltip/_pointseries_tooltip.html | 12 -
.../tooltip/_pointseries_tooltip_formatter.js | 89 -----
.../ui/public/vis/vis_types/_index.scss | 2 -
.../translations/translations/ja-JP.json | 372 +++++++++---------
.../translations/translations/zh-CN.json | 372 +++++++++---------
yarn.lock | 5 +
124 files changed, 1415 insertions(+), 1707 deletions(-)
rename src/legacy/{ui/public/vis/components/tooltip/__tests__ => core_plugins/vis_type_vislib/public/vislib/__tests__/components}/_tooltip_formatter.js (88%)
rename src/legacy/{ui/public/vis/components/tooltip/__tests__ => core_plugins/vis_type_vislib/public/vislib/__tests__/components}/positioning.js (99%)
rename src/legacy/{ui/public/vis/vis_types => core_plugins/vis_type_vislib/public/vislib}/_vislib_vis_type.scss (99%)
rename src/legacy/{ui/public/vis/vis_types/vislib_vis_legend/__snapshots__/vislib_vis_legend.test.tsx.snap => core_plugins/vis_type_vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap} (100%)
create mode 100644 src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/_index.scss
rename src/legacy/{ui/public/vis/vis_types/_vislib_vis_legend.scss => core_plugins/vis_type_vislib/public/vislib/components/legend/_legend.scss} (85%)
rename src/legacy/{ui/public/vis/vis_types/vislib_vis_legend => core_plugins/vis_type_vislib/public/vislib/components/legend}/index.ts (94%)
rename src/legacy/{ui/public/vis/vis_types/vislib_vis_legend/vislib_vis_legend.test.tsx => core_plugins/vis_type_vislib/public/vislib/components/legend/legend.test.tsx} (96%)
rename src/legacy/{ui/public/vis/vis_types/vislib_vis_legend/vislib_vis_legend.tsx => core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx} (93%)
rename src/legacy/{ui/public/vis/vis_types/vislib_vis_legend/vislib_vis_legend_item.tsx => core_plugins/vis_type_vislib/public/vislib/components/legend/legend_item.tsx} (92%)
rename src/legacy/{ui/public/vis/vis_types/vislib_vis_legend => core_plugins/vis_type_vislib/public/vislib/components/legend}/models.ts (100%)
rename src/legacy/{ui/public/vis/vis_types/vislib_vis_legend => core_plugins/vis_type_vislib/public/vislib/components/legend}/pie_utils.ts (100%)
rename src/legacy/{ui/public/vis => core_plugins/vis_type_vislib/public/vislib}/components/tooltip/_collect_branch.js (100%)
rename src/legacy/{ui/public/vis => core_plugins/vis_type_vislib/public/vislib}/components/tooltip/_collect_branch.test.js (100%)
create mode 100644 src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_hierarchical_tooltip_formatter.js
rename src/legacy/{ui/public/vis => core_plugins/vis_type_vislib/public/vislib}/components/tooltip/_index.scss (100%)
create mode 100644 src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js
rename src/legacy/{ui/public/vis => core_plugins/vis_type_vislib/public/vislib}/components/tooltip/_tooltip.scss (97%)
rename src/legacy/{ui/public/vis => core_plugins/vis_type_vislib/public/vislib}/components/tooltip/index.js (80%)
rename src/legacy/{ui/public/vis => core_plugins/vis_type_vislib/public/vislib}/components/tooltip/position_tooltip.js (100%)
rename src/legacy/{ui/public/vis => core_plugins/vis_type_vislib/public/vislib}/components/tooltip/tooltip.js (97%)
delete mode 100644 src/legacy/ui/public/vis/components/_index.scss
delete mode 100644 src/legacy/ui/public/vis/components/tooltip/_hierarchical_tooltip.html
delete mode 100644 src/legacy/ui/public/vis/components/tooltip/_hierarchical_tooltip_formatter.js
delete mode 100644 src/legacy/ui/public/vis/components/tooltip/_pointseries_tooltip.html
delete mode 100644 src/legacy/ui/public/vis/components/tooltip/_pointseries_tooltip_formatter.js
delete mode 100644 src/legacy/ui/public/vis/vis_types/_index.scss
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index ed5721e8756e8..7276a726fd6d1 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -15,6 +15,7 @@
/src/legacy/core_plugins/kibana/public/home/ @elastic/kibana-app
/src/legacy/core_plugins/kibana/public/dev_tools/ @elastic/kibana-app
/src/legacy/core_plugins/metrics/ @elastic/kibana-app
+/src/legacy/core_plugins/vis_type_vislib/ @elastic/kibana-app
/src/plugins/home/ @elastic/kibana-app
/src/plugins/kibana_legacy/ @elastic/kibana-app
/src/plugins/timelion/ @elastic/kibana-app
diff --git a/.i18nrc.json b/.i18nrc.json
index 73acf92cda149..907310b32e35c 100644
--- a/.i18nrc.json
+++ b/.i18nrc.json
@@ -21,7 +21,6 @@
"interpreter": "src/legacy/core_plugins/interpreter",
"kbn": "src/legacy/core_plugins/kibana",
"kbnDocViews": "src/legacy/core_plugins/kbn_doc_views",
- "kbnVislibVisTypes": "src/legacy/core_plugins/vis_type_vislib",
"management": ["src/legacy/core_plugins/management", "src/plugins/management"],
"kibana_react": "src/legacy/core_plugins/kibana_react",
"kibana-react": "src/plugins/kibana_react",
@@ -41,6 +40,7 @@
"visTypeTagCloud": "src/legacy/core_plugins/vis_type_tagcloud",
"visTypeTimeseries": "src/legacy/core_plugins/vis_type_timeseries",
"visTypeVega": "src/legacy/core_plugins/vis_type_vega",
+ "visTypeVislib": "src/legacy/core_plugins/vis_type_vislib",
"visualizations": [
"src/plugins/visualizations",
"src/legacy/core_plugins/visualizations"
@@ -50,4 +50,4 @@
"src/legacy/ui/ui_render/ui_render_mixin.js"
],
"translations": []
-}
\ No newline at end of file
+}
diff --git a/package.json b/package.json
index 430ab9e1ba77d..8b96fcc9d396e 100644
--- a/package.json
+++ b/package.json
@@ -347,6 +347,7 @@
"@types/moment-timezone": "^0.5.12",
"@types/mustache": "^0.8.31",
"@types/node": "^10.12.27",
+ "@types/numeral": "^0.0.26",
"@types/opn": "^5.1.0",
"@types/pegjs": "^0.10.1",
"@types/pngjs": "^3.3.2",
diff --git a/renovate.json5 b/renovate.json5
index 5af62d0acef85..6764ed38ba4cf 100644
--- a/renovate.json5
+++ b/renovate.json5
@@ -657,6 +657,14 @@
'@types/nodemailer',
],
},
+ {
+ groupSlug: 'numeral',
+ groupName: 'numeral related packages',
+ packageNames: [
+ 'numeral',
+ '@types/numeral',
+ ],
+ },
{
groupSlug: 'object-hash',
groupName: 'object-hash related packages',
diff --git a/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx b/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx
index 73fe07ec60102..287fb87e735a2 100644
--- a/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx
+++ b/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx
@@ -24,11 +24,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { FileLayerField, VectorLayer, ServiceSettings } from 'ui/vis/map/service_settings';
import { VisOptionsProps } from 'ui/vis/editors/default';
-import {
- NumberInputOption,
- SelectOption,
- SwitchOption,
-} from '../../../vis_type_vislib/public/components';
+import { NumberInputOption, SelectOption, SwitchOption } from '../../../vis_type_vislib/public';
import { WmsOptions } from '../../../tile_map/public/components/wms_options';
import { RegionMapVisParams } from '../types';
diff --git a/src/legacy/core_plugins/tile_map/public/components/tile_map_options.tsx b/src/legacy/core_plugins/tile_map/public/components/tile_map_options.tsx
index e57cea8467d12..4ab9f95ee4c3c 100644
--- a/src/legacy/core_plugins/tile_map/public/components/tile_map_options.tsx
+++ b/src/legacy/core_plugins/tile_map/public/components/tile_map_options.tsx
@@ -27,7 +27,7 @@ import {
RangeOption,
SelectOption,
SwitchOption,
-} from '../../../vis_type_vislib/public/components';
+} from '../../../vis_type_vislib/public';
import { WmsOptions } from './wms_options';
import { TileMapVisParams } from '../types';
import { MapTypes } from '../map_types';
diff --git a/src/legacy/core_plugins/tile_map/public/components/wms_internal_options.tsx b/src/legacy/core_plugins/tile_map/public/components/wms_internal_options.tsx
index 2989f6ce7ebd5..b81667400303d 100644
--- a/src/legacy/core_plugins/tile_map/public/components/wms_internal_options.tsx
+++ b/src/legacy/core_plugins/tile_map/public/components/wms_internal_options.tsx
@@ -21,7 +21,7 @@ import React from 'react';
import { EuiLink, EuiSpacer, EuiText, EuiScreenReaderOnly } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
-import { TextInputOption } from '../../../vis_type_vislib/public/components';
+import { TextInputOption } from '../../../vis_type_vislib/public';
import { WMSOptions } from '../types';
interface WmsInternalOptions {
diff --git a/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx b/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx
index d9dca5afd7377..a0b7a0a844f55 100644
--- a/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx
+++ b/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx
@@ -25,7 +25,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { TmsLayer } from 'ui/vis/map/service_settings';
import { Vis } from 'ui/vis';
import { RegionMapVisParams } from '../../../region_map/public/types';
-import { SelectOption, SwitchOption } from '../../../vis_type_vislib/public/components';
+import { SelectOption, SwitchOption } from '../../../vis_type_vislib/public';
import { WmsInternalOptions } from './wms_internal_options';
import { WMSOptions, TileMapVisParams } from '../types';
diff --git a/src/legacy/core_plugins/vis_type_markdown/public/settings_options.tsx b/src/legacy/core_plugins/vis_type_markdown/public/settings_options.tsx
index 125577815c207..18852b549b1ed 100644
--- a/src/legacy/core_plugins/vis_type_markdown/public/settings_options.tsx
+++ b/src/legacy/core_plugins/vis_type_markdown/public/settings_options.tsx
@@ -22,7 +22,7 @@ import { EuiPanel } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { VisOptionsProps } from 'ui/vis/editors/default';
-import { RangeOption, SwitchOption } from '../../vis_type_vislib/public/components';
+import { RangeOption, SwitchOption } from '../../vis_type_vislib/public';
import { MarkdownVisParams } from './types';
function SettingsOptions({ stateParams, setValue }: VisOptionsProps) {
diff --git a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_options.tsx b/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_options.tsx
index 032f66d92624c..e144c055d8023 100644
--- a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_options.tsx
+++ b/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_options.tsx
@@ -31,13 +31,13 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { VisOptionsProps } from '../legacy_imports';
import {
+ ColorModes,
ColorRanges,
ColorSchemaOptions,
SwitchOption,
RangeOption,
SetColorSchemaOptionsValue,
-} from '../../../vis_type_vislib/public/components';
-import { ColorModes } from '../../../vis_type_vislib/public/utils/collections';
+} from '../../../vis_type_vislib/public';
import { MetricVisParam, VisParams } from '../types';
import { SetColorRangeValue } from '../../../vis_type_vislib/public/components/common/color_ranges';
diff --git a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.ts b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.ts
index 45110ca113003..4ebd8bf97c55a 100644
--- a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.ts
+++ b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.ts
@@ -27,7 +27,7 @@ import {
Render,
Style,
} from '../../../../plugins/expressions/public';
-import { ColorModes } from '../../vis_type_vislib/public/utils/collections';
+import { ColorModes } from '../../vis_type_vislib/public';
import { visType, DimensionsVisParam, VisParams } from './types';
export type Context = KibanaDatatable;
diff --git a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts
index 0d9019ee0579c..ee7ead0b7331b 100644
--- a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts
+++ b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts
@@ -21,8 +21,8 @@ import { i18n } from '@kbn/i18n';
import { MetricVisComponent } from './components/metric_vis_component';
import { MetricVisOptions } from './components/metric_vis_options';
+import { ColorModes } from '../../vis_type_vislib/public';
import { Schemas, AggGroupNames, colorSchemas, ColorSchemas } from './legacy_imports';
-import { ColorModes } from '../../vis_type_vislib/public/utils/collections';
export const metricVisTypeDefinition = {
name: 'metric',
diff --git a/src/legacy/core_plugins/vis_type_metric/public/types.ts b/src/legacy/core_plugins/vis_type_metric/public/types.ts
index 71c1c12b4f8f0..34cb1b209a3ae 100644
--- a/src/legacy/core_plugins/vis_type_metric/public/types.ts
+++ b/src/legacy/core_plugins/vis_type_metric/public/types.ts
@@ -20,8 +20,7 @@
import { ColorSchemas } from './legacy_imports';
import { Range } from '../../../../plugins/expressions/public';
import { SchemaConfig } from '../../visualizations/public';
-import { ColorModes } from '../../vis_type_vislib/public/utils/collections';
-import { Labels, Style } from '../../vis_type_vislib/public/types';
+import { ColorModes, Labels, Style } from '../../vis_type_vislib/public';
export const visType = 'metric';
diff --git a/src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx b/src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx
index 33d7480de5a8e..529439a800682 100644
--- a/src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx
+++ b/src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx
@@ -24,11 +24,7 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { tabifyGetColumns, VisOptionsProps } from '../legacy_imports';
-import {
- NumberInputOption,
- SwitchOption,
- SelectOption,
-} from '../../../vis_type_vislib/public/components/common';
+import { NumberInputOption, SwitchOption, SelectOption } from '../../../vis_type_vislib/public';
import { TableVisParams } from '../types';
import { totalAggregations, isAggConfigNumeric } from './utils';
diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx
index c500b5d888b05..76117c8b6b398 100644
--- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx
+++ b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx
@@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n';
import { ValidatedDualRange } from 'ui/validated_range';
import { VisOptionsProps } from 'ui/vis/editors/default';
-import { SelectOption, SwitchOption } from '../../../vis_type_vislib/public/components';
+import { SelectOption, SwitchOption } from '../../../vis_type_vislib/public';
import { TagCloudVisParams } from '../types';
function TagCloudOptions({ stateParams, setValue, vis }: VisOptionsProps) {
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/area.ts b/src/legacy/core_plugins/vis_type_vislib/public/area.ts
index 9484ddc16fe62..a96fb19a9321c 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/area.ts
+++ b/src/legacy/core_plugins/vis_type_vislib/public/area.ts
@@ -42,9 +42,9 @@ import { KbnVislibVisTypesDependencies } from './plugin';
export const createAreaVisTypeDefinition = (deps: KbnVislibVisTypesDependencies) => ({
name: 'area',
- title: i18n.translate('kbnVislibVisTypes.area.areaTitle', { defaultMessage: 'Area' }),
+ title: i18n.translate('visTypeVislib.area.areaTitle', { defaultMessage: 'Area' }),
icon: 'visArea',
- description: i18n.translate('kbnVislibVisTypes.area.areaDescription', {
+ description: i18n.translate('visTypeVislib.area.areaDescription', {
defaultMessage: 'Emphasize the quantity beneath a line chart',
}),
visualization: createVislibVisController(deps),
@@ -136,7 +136,7 @@ export const createAreaVisTypeDefinition = (deps: KbnVislibVisTypesDependencies)
{
group: AggGroupNames.Metrics,
name: 'metric',
- title: i18n.translate('kbnVislibVisTypes.area.metricsTitle', {
+ title: i18n.translate('visTypeVislib.area.metricsTitle', {
defaultMessage: 'Y-axis',
}),
aggFilter: ['!geo_centroid', '!geo_bounds'],
@@ -146,7 +146,7 @@ export const createAreaVisTypeDefinition = (deps: KbnVislibVisTypesDependencies)
{
group: AggGroupNames.Metrics,
name: 'radius',
- title: i18n.translate('kbnVislibVisTypes.area.radiusTitle', {
+ title: i18n.translate('visTypeVislib.area.radiusTitle', {
defaultMessage: 'Dot size',
}),
min: 0,
@@ -156,7 +156,7 @@ export const createAreaVisTypeDefinition = (deps: KbnVislibVisTypesDependencies)
{
group: AggGroupNames.Buckets,
name: 'segment',
- title: i18n.translate('kbnVislibVisTypes.area.segmentTitle', {
+ title: i18n.translate('visTypeVislib.area.segmentTitle', {
defaultMessage: 'X-axis',
}),
min: 0,
@@ -166,7 +166,7 @@ export const createAreaVisTypeDefinition = (deps: KbnVislibVisTypesDependencies)
{
group: AggGroupNames.Buckets,
name: 'group',
- title: i18n.translate('kbnVislibVisTypes.area.groupTitle', {
+ title: i18n.translate('visTypeVislib.area.groupTitle', {
defaultMessage: 'Split series',
}),
min: 0,
@@ -176,7 +176,7 @@ export const createAreaVisTypeDefinition = (deps: KbnVislibVisTypesDependencies)
{
group: AggGroupNames.Buckets,
name: 'split',
- title: i18n.translate('kbnVislibVisTypes.area.splitTitle', {
+ title: i18n.translate('visTypeVislib.area.splitTitle', {
defaultMessage: 'Split chart',
}),
min: 0,
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/common/basic_options.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/common/basic_options.tsx
index 81174d63060e5..229945621fe76 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/components/common/basic_options.tsx
+++ b/src/legacy/core_plugins/vis_type_vislib/public/components/common/basic_options.tsx
@@ -37,7 +37,7 @@ function BasicOptions({
return (
<>
({
setValue={setValue}
/>
@@ -79,11 +79,11 @@ function ColorSchemaOptions({
disabled={disabled}
helpText={
showHelpText &&
- i18n.translate('kbnVislibVisTypes.controls.colorSchema.howToChangeColorsDescription', {
+ i18n.translate('visTypeVislib.controls.colorSchema.howToChangeColorsDescription', {
defaultMessage: 'Individual colors can be changed in the legend.',
})
}
- label={i18n.translate('kbnVislibVisTypes.controls.colorSchema.colorSchemaLabel', {
+ label={i18n.translate('visTypeVislib.controls.colorSchema.colorSchemaLabel', {
defaultMessage: 'Color schema',
})}
labelAppend={isCustomColors && resetColorsButton}
@@ -95,7 +95,7 @@ function ColorSchemaOptions({
({
const [stateValue, setStateValue] = useState(value);
const [isValidState, setIsValidState] = useState(true);
- const error = i18n.translate('kbnVislibVisTypes.controls.rangeErrorMessage', {
+ const error = i18n.translate('visTypeVislib.controls.rangeErrorMessage', {
defaultMessage: 'Values must be on or between {min} and {max}',
values: { min, max },
});
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/common/truncate_labels.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/common/truncate_labels.tsx
index 81772107bc729..2f0cb701848d0 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/components/common/truncate_labels.tsx
+++ b/src/legacy/core_plugins/vis_type_vislib/public/components/common/truncate_labels.tsx
@@ -33,7 +33,7 @@ function TruncateLabelsOption({ disabled, value = null, setValue }: TruncateLabe
return (
@@ -39,7 +39,7 @@ function LabelsPanel({ stateParams, setValue, setGaugeValue }: GaugeOptionsInter
@@ -76,10 +76,10 @@ function RangesPanel({
@@ -44,7 +44,7 @@ function StylePanel({ aggs, setGaugeValue, stateParams, vis }: GaugeOptionsInter
) {
@@ -78,13 +78,13 @@ function HeatmapOptions(props: VisOptionsProps) {
) {
@@ -114,7 +114,7 @@ function HeatmapOptions(props: VisOptionsProps) {
) {
/>
) {
) {
data-test-subj="heatmapColorsNumber"
disabled={stateParams.setColorRange}
isInvalid={isColorsNumberInvalid}
- label={i18n.translate('kbnVislibVisTypes.controls.heatmapOptions.colorsNumberLabel', {
+ label={i18n.translate('visTypeVislib.controls.heatmapOptions.colorsNumberLabel', {
defaultMessage: 'Number of colors',
})}
max={10}
@@ -160,7 +159,7 @@ function HeatmapOptions(props: VisOptionsProps) {
@@ -73,7 +73,7 @@ function LabelsPanel({ valueAxis, setValue }: LabelsPanelProps) {
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap
index d88654cfdc0c4..124b7d7004c0d 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap
+++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap
@@ -10,7 +10,7 @@ exports[`CategoryAxisPanel component should init with the default set of props 1
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap
index 027c074176ef8..8972cdbd82fc3 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap
+++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap
@@ -11,7 +11,7 @@ exports[`LabelOptions component should init with the default set of props 1`] =
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/value_axes_panel.test.tsx.snap b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/value_axes_panel.test.tsx.snap
index 8d20765fe3591..f589a69eecbc3 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/value_axes_panel.test.tsx.snap
+++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/value_axes_panel.test.tsx.snap
@@ -18,7 +18,7 @@ exports[`ValueAxesPanel component should init with the default set of props 1`]
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.tsx
index b83508f3f0896..a698d70d97232 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.tsx
+++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.tsx
@@ -62,7 +62,7 @@ function CategoryAxisPanel(props: CategoryAxisPanelProps) {
@@ -70,7 +70,7 @@ function CategoryAxisPanel(props: CategoryAxisPanelProps) {
@@ -73,12 +73,9 @@ function LabelOptions({ stateParams, setValue, axis, axesName, index }: LabelOpt
@@ -57,7 +57,7 @@ function SeriesPanel(props: SeriesPanelProps) {
buttonContent={chart.data.label}
buttonContentClassName="visEditorSidebar__aggGroupAccordionButtonContent eui-textTruncate"
aria-label={i18n.translate(
- 'kbnVislibVisTypes.controls.pointSeries.seriesAccordionAriaLabel',
+ 'visTypeVislib.controls.pointSeries.seriesAccordionAriaLabel',
{
defaultMessage: 'Toggle {agg} options',
values: { agg: chart.data.label },
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx
index eb0ab4333af59..b94f5ebbcce44 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx
+++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx
@@ -60,7 +60,7 @@ function ValueAxesPanel(props: ValueAxesPanelProps) {
const removeButtonTooltip = useMemo(
() =>
- i18n.translate('kbnVislibVisTypes.controls.pointSeries.valueAxes.removeButtonTooltip', {
+ i18n.translate('visTypeVislib.controls.pointSeries.valueAxes.removeButtonTooltip', {
defaultMessage: 'Remove Y-axis',
}),
[]
@@ -83,7 +83,7 @@ function ValueAxesPanel(props: ValueAxesPanelProps) {
const addButtonTooltip = useMemo(
() =>
- i18n.translate('kbnVislibVisTypes.controls.pointSeries.valueAxes.addButtonTooltip', {
+ i18n.translate('visTypeVislib.controls.pointSeries.valueAxes.addButtonTooltip', {
defaultMessage: 'Add Y-axis',
}),
[]
@@ -111,7 +111,7 @@ function ValueAxesPanel(props: ValueAxesPanelProps) {
@@ -142,7 +142,7 @@ function ValueAxesPanel(props: ValueAxesPanelProps) {
buttonClassName="eui-textTruncate"
buttonContentClassName="visEditorSidebar__aggGroupAccordionButtonContent eui-textTruncate"
aria-label={i18n.translate(
- 'kbnVislibVisTypes.controls.pointSeries.valueAxes.toggleOptionsAriaLabel',
+ 'visTypeVislib.controls.pointSeries.valueAxes.toggleOptionsAriaLabel',
{
defaultMessage: 'Toggle {axisName} options',
values: { axisName: axis.name },
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx
index b4ea4cb42ee60..5daea193b06c1 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx
+++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx
@@ -119,7 +119,7 @@ function ValueAxisOptions(props: ValueAxisOptionsParams) {
return (
<>
) {
) {
) {
setValue={setLabels}
/>
) {
setValue={setLabels}
/>
@@ -72,14 +72,14 @@ function GridPanel({ stateParams, setValue, hasHistogramAgg }: VisOptionsProps)
@@ -46,7 +46,7 @@ function PointSeriesOptions(props: ValidationVisOptionsProps)
{vis.hasSchemaAgg('segment', 'date_histogram') ? (
)
/>
) : (
)
{vis.type.type === 'histogram' && (
@@ -63,7 +63,7 @@ function ThresholdPanel({
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/gauge.ts b/src/legacy/core_plugins/vis_type_vislib/public/gauge.ts
index 5dcc8ad16918d..f235ed4bb04e4 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/gauge.ts
+++ b/src/legacy/core_plugins/vis_type_vislib/public/gauge.ts
@@ -56,9 +56,9 @@ export interface GaugeVisParams {
export const createGaugeVisTypeDefinition = (deps: KbnVislibVisTypesDependencies) => ({
name: 'gauge',
- title: i18n.translate('kbnVislibVisTypes.gauge.gaugeTitle', { defaultMessage: 'Gauge' }),
+ title: i18n.translate('visTypeVislib.gauge.gaugeTitle', { defaultMessage: 'Gauge' }),
icon: 'visGauge',
- description: i18n.translate('kbnVislibVisTypes.gauge.gaugeDescription', {
+ description: i18n.translate('visTypeVislib.gauge.gaugeDescription', {
defaultMessage:
"Gauges indicate the status of a metric. Use it to show how a metric's value relates to reference threshold values.",
}),
@@ -116,7 +116,7 @@ export const createGaugeVisTypeDefinition = (deps: KbnVislibVisTypesDependencies
{
group: AggGroupNames.Metrics,
name: 'metric',
- title: i18n.translate('kbnVislibVisTypes.gauge.metricTitle', { defaultMessage: 'Metric' }),
+ title: i18n.translate('visTypeVislib.gauge.metricTitle', { defaultMessage: 'Metric' }),
min: 1,
aggFilter: [
'!std_dev',
@@ -134,7 +134,7 @@ export const createGaugeVisTypeDefinition = (deps: KbnVislibVisTypesDependencies
{
group: AggGroupNames.Buckets,
name: 'group',
- title: i18n.translate('kbnVislibVisTypes.gauge.groupTitle', {
+ title: i18n.translate('visTypeVislib.gauge.groupTitle', {
defaultMessage: 'Split group',
}),
min: 0,
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/goal.ts b/src/legacy/core_plugins/vis_type_vislib/public/goal.ts
index 302d5f6393ef9..94262629669be 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/goal.ts
+++ b/src/legacy/core_plugins/vis_type_vislib/public/goal.ts
@@ -27,9 +27,9 @@ import { KbnVislibVisTypesDependencies } from './plugin';
export const createGoalVisTypeDefinition = (deps: KbnVislibVisTypesDependencies) => ({
name: 'goal',
- title: i18n.translate('kbnVislibVisTypes.goal.goalTitle', { defaultMessage: 'Goal' }),
+ title: i18n.translate('visTypeVislib.goal.goalTitle', { defaultMessage: 'Goal' }),
icon: 'visGoal',
- description: i18n.translate('kbnVislibVisTypes.goal.goalDescription', {
+ description: i18n.translate('visTypeVislib.goal.goalDescription', {
defaultMessage: 'A goal chart indicates how close you are to your final goal.',
}),
visualization: createVislibVisController(deps),
@@ -80,7 +80,7 @@ export const createGoalVisTypeDefinition = (deps: KbnVislibVisTypesDependencies)
{
group: AggGroupNames.Metrics,
name: 'metric',
- title: i18n.translate('kbnVislibVisTypes.goal.metricTitle', { defaultMessage: 'Metric' }),
+ title: i18n.translate('visTypeVislib.goal.metricTitle', { defaultMessage: 'Metric' }),
min: 1,
aggFilter: [
'!std_dev',
@@ -98,7 +98,7 @@ export const createGoalVisTypeDefinition = (deps: KbnVislibVisTypesDependencies)
{
group: AggGroupNames.Buckets,
name: 'group',
- title: i18n.translate('kbnVislibVisTypes.goal.groupTitle', {
+ title: i18n.translate('visTypeVislib.goal.groupTitle', {
defaultMessage: 'Split group',
}),
min: 0,
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/heatmap.ts b/src/legacy/core_plugins/vis_type_vislib/public/heatmap.ts
index eb5f84b409838..470e978027ca2 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/heatmap.ts
+++ b/src/legacy/core_plugins/vis_type_vislib/public/heatmap.ts
@@ -41,9 +41,9 @@ export interface HeatmapVisParams extends CommonVislibParams, ColorSchemaVislibP
export const createHeatmapVisTypeDefinition = (deps: KbnVislibVisTypesDependencies) => ({
name: 'heatmap',
- title: i18n.translate('kbnVislibVisTypes.heatmap.heatmapTitle', { defaultMessage: 'Heat Map' }),
+ title: i18n.translate('visTypeVislib.heatmap.heatmapTitle', { defaultMessage: 'Heat Map' }),
icon: 'visHeatmap',
- description: i18n.translate('kbnVislibVisTypes.heatmap.heatmapDescription', {
+ description: i18n.translate('visTypeVislib.heatmap.heatmapDescription', {
defaultMessage: 'Shade cells within a matrix',
}),
visualization: createVislibVisController(deps),
@@ -90,7 +90,7 @@ export const createHeatmapVisTypeDefinition = (deps: KbnVislibVisTypesDependenci
{
group: AggGroupNames.Metrics,
name: 'metric',
- title: i18n.translate('kbnVislibVisTypes.heatmap.metricTitle', { defaultMessage: 'Value' }),
+ title: i18n.translate('visTypeVislib.heatmap.metricTitle', { defaultMessage: 'Value' }),
min: 1,
max: 1,
aggFilter: [
@@ -109,7 +109,7 @@ export const createHeatmapVisTypeDefinition = (deps: KbnVislibVisTypesDependenci
{
group: AggGroupNames.Buckets,
name: 'segment',
- title: i18n.translate('kbnVislibVisTypes.heatmap.segmentTitle', {
+ title: i18n.translate('visTypeVislib.heatmap.segmentTitle', {
defaultMessage: 'X-axis',
}),
min: 0,
@@ -119,7 +119,7 @@ export const createHeatmapVisTypeDefinition = (deps: KbnVislibVisTypesDependenci
{
group: AggGroupNames.Buckets,
name: 'group',
- title: i18n.translate('kbnVislibVisTypes.heatmap.groupTitle', { defaultMessage: 'Y-axis' }),
+ title: i18n.translate('visTypeVislib.heatmap.groupTitle', { defaultMessage: 'Y-axis' }),
min: 0,
max: 1,
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
@@ -127,7 +127,7 @@ export const createHeatmapVisTypeDefinition = (deps: KbnVislibVisTypesDependenci
{
group: AggGroupNames.Buckets,
name: 'split',
- title: i18n.translate('kbnVislibVisTypes.heatmap.splitTitle', {
+ title: i18n.translate('visTypeVislib.heatmap.splitTitle', {
defaultMessage: 'Split chart',
}),
min: 0,
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/histogram.ts b/src/legacy/core_plugins/vis_type_vislib/public/histogram.ts
index f92875a62cfd7..490b557b7bafb 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/histogram.ts
+++ b/src/legacy/core_plugins/vis_type_vislib/public/histogram.ts
@@ -42,11 +42,11 @@ import { KbnVislibVisTypesDependencies } from './plugin';
export const createHistogramVisTypeDefinition = (deps: KbnVislibVisTypesDependencies) => ({
name: 'histogram',
- title: i18n.translate('kbnVislibVisTypes.histogram.histogramTitle', {
+ title: i18n.translate('visTypeVislib.histogram.histogramTitle', {
defaultMessage: 'Vertical Bar',
}),
icon: 'visBarVertical',
- description: i18n.translate('kbnVislibVisTypes.histogram.histogramDescription', {
+ description: i18n.translate('visTypeVislib.histogram.histogramDescription', {
defaultMessage: 'Assign a continuous variable to each axis',
}),
visualization: createVislibVisController(deps),
@@ -139,7 +139,7 @@ export const createHistogramVisTypeDefinition = (deps: KbnVislibVisTypesDependen
{
group: AggGroupNames.Metrics,
name: 'metric',
- title: i18n.translate('kbnVislibVisTypes.histogram.metricTitle', {
+ title: i18n.translate('visTypeVislib.histogram.metricTitle', {
defaultMessage: 'Y-axis',
}),
min: 1,
@@ -149,7 +149,7 @@ export const createHistogramVisTypeDefinition = (deps: KbnVislibVisTypesDependen
{
group: AggGroupNames.Metrics,
name: 'radius',
- title: i18n.translate('kbnVislibVisTypes.histogram.radiusTitle', {
+ title: i18n.translate('visTypeVislib.histogram.radiusTitle', {
defaultMessage: 'Dot size',
}),
min: 0,
@@ -159,7 +159,7 @@ export const createHistogramVisTypeDefinition = (deps: KbnVislibVisTypesDependen
{
group: AggGroupNames.Buckets,
name: 'segment',
- title: i18n.translate('kbnVislibVisTypes.histogram.segmentTitle', {
+ title: i18n.translate('visTypeVislib.histogram.segmentTitle', {
defaultMessage: 'X-axis',
}),
min: 0,
@@ -169,7 +169,7 @@ export const createHistogramVisTypeDefinition = (deps: KbnVislibVisTypesDependen
{
group: AggGroupNames.Buckets,
name: 'group',
- title: i18n.translate('kbnVislibVisTypes.histogram.groupTitle', {
+ title: i18n.translate('visTypeVislib.histogram.groupTitle', {
defaultMessage: 'Split series',
}),
min: 0,
@@ -179,7 +179,7 @@ export const createHistogramVisTypeDefinition = (deps: KbnVislibVisTypesDependen
{
group: AggGroupNames.Buckets,
name: 'split',
- title: i18n.translate('kbnVislibVisTypes.histogram.splitTitle', {
+ title: i18n.translate('visTypeVislib.histogram.splitTitle', {
defaultMessage: 'Split chart',
}),
min: 0,
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/horizontal_bar.ts b/src/legacy/core_plugins/vis_type_vislib/public/horizontal_bar.ts
index ada0c6b44ff70..e8d51fe037a63 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/horizontal_bar.ts
+++ b/src/legacy/core_plugins/vis_type_vislib/public/horizontal_bar.ts
@@ -42,11 +42,11 @@ import { KbnVislibVisTypesDependencies } from './plugin';
export const createHorizontalBarVisTypeDefinition = (deps: KbnVislibVisTypesDependencies) => ({
name: 'horizontal_bar',
- title: i18n.translate('kbnVislibVisTypes.horizontalBar.horizontalBarTitle', {
+ title: i18n.translate('visTypeVislib.horizontalBar.horizontalBarTitle', {
defaultMessage: 'Horizontal Bar',
}),
icon: 'visBarHorizontal',
- description: i18n.translate('kbnVislibVisTypes.horizontalBar.horizontalBarDescription', {
+ description: i18n.translate('visTypeVislib.horizontalBar.horizontalBarDescription', {
defaultMessage: 'Assign a continuous variable to each axis',
}),
visualization: createVislibVisController(deps),
@@ -138,7 +138,7 @@ export const createHorizontalBarVisTypeDefinition = (deps: KbnVislibVisTypesDepe
{
group: AggGroupNames.Metrics,
name: 'metric',
- title: i18n.translate('kbnVislibVisTypes.horizontalBar.metricTitle', {
+ title: i18n.translate('visTypeVislib.horizontalBar.metricTitle', {
defaultMessage: 'Y-axis',
}),
min: 1,
@@ -148,7 +148,7 @@ export const createHorizontalBarVisTypeDefinition = (deps: KbnVislibVisTypesDepe
{
group: AggGroupNames.Metrics,
name: 'radius',
- title: i18n.translate('kbnVislibVisTypes.horizontalBar.radiusTitle', {
+ title: i18n.translate('visTypeVislib.horizontalBar.radiusTitle', {
defaultMessage: 'Dot size',
}),
min: 0,
@@ -158,7 +158,7 @@ export const createHorizontalBarVisTypeDefinition = (deps: KbnVislibVisTypesDepe
{
group: AggGroupNames.Buckets,
name: 'segment',
- title: i18n.translate('kbnVislibVisTypes.horizontalBar.segmentTitle', {
+ title: i18n.translate('visTypeVislib.horizontalBar.segmentTitle', {
defaultMessage: 'X-axis',
}),
min: 0,
@@ -168,7 +168,7 @@ export const createHorizontalBarVisTypeDefinition = (deps: KbnVislibVisTypesDepe
{
group: AggGroupNames.Buckets,
name: 'group',
- title: i18n.translate('kbnVislibVisTypes.horizontalBar.groupTitle', {
+ title: i18n.translate('visTypeVislib.horizontalBar.groupTitle', {
defaultMessage: 'Split series',
}),
min: 0,
@@ -178,7 +178,7 @@ export const createHorizontalBarVisTypeDefinition = (deps: KbnVislibVisTypesDepe
{
group: AggGroupNames.Buckets,
name: 'split',
- title: i18n.translate('kbnVislibVisTypes.horizontalBar.splitTitle', {
+ title: i18n.translate('visTypeVislib.horizontalBar.splitTitle', {
defaultMessage: 'Split chart',
}),
min: 0,
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/index.ts b/src/legacy/core_plugins/vis_type_vislib/public/index.ts
index 3b4bcb6bc3a7e..80c078de1a10b 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/index.ts
+++ b/src/legacy/core_plugins/vis_type_vislib/public/index.ts
@@ -24,4 +24,18 @@ export function plugin(initializerContext: PluginInitializerContext) {
return new Plugin(initializerContext);
}
+export {
+ BasicOptions,
+ RangeOption,
+ ColorRanges,
+ SelectOption,
+ SetColorSchemaOptionsValue,
+ ColorSchemaOptions,
+ NumberInputOption,
+ SwitchOption,
+ TextInputOption,
+} from './components';
+
export { ColorModes } from './utils/collections';
+
+export * from './types';
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts b/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts
index 3d4cf55adc5e0..775cf63b96f68 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts
+++ b/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts
@@ -21,16 +21,6 @@ import { npSetup, npStart } from 'ui/new_platform';
import { PluginInitializerContext } from 'kibana/public';
/* eslint-disable prettier/prettier */
-import {
- initializeHierarchicalTooltipFormatter,
- getHierarchicalTooltipFormatter,
- // @ts-ignore
-} from 'ui/vis/components/tooltip/_hierarchical_tooltip_formatter';
-import {
- initializePointSeriesTooltipFormatter,
- getPointSeriesTooltipFormatter,
- // @ts-ignore
-} from 'ui/vis/components/tooltip/_pointseries_tooltip_formatter';
import {
vislibSeriesResponseHandlerProvider,
vislibSlicesResponseHandlerProvider,
@@ -53,10 +43,6 @@ const setupPlugins: Readonly = {
expressions: npSetup.plugins.expressions,
visualizations: visualizationsSetup,
__LEGACY: {
- initializeHierarchicalTooltipFormatter,
- getHierarchicalTooltipFormatter,
- initializePointSeriesTooltipFormatter,
- getPointSeriesTooltipFormatter,
vislibSeriesResponseHandlerProvider,
vislibSlicesResponseHandlerProvider,
vislibColor,
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts b/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts
index 2970942f221e8..6b10fb0d41223 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts
+++ b/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts
@@ -23,13 +23,10 @@ export { RangeValues, RangesParamEditor } from 'ui/vis/editors/default/controls/
export { ColorSchema, ColorSchemas, colorSchemas, getHeatmapColors } from 'ui/color_maps';
export { AggConfig, Vis, VisParams } from 'ui/vis';
export { AggType } from 'ui/agg_types';
-export { CUSTOM_LEGEND_VIS_TYPES, VisLegend } from 'ui/vis/vis_types/vislib_vis_legend';
-// @ts-ignore
-export { Tooltip } from 'ui/vis/components/tooltip';
// @ts-ignore
export { SimpleEmitter } from 'ui/utils/simple_emitter';
// @ts-ignore
export { Binder } from 'ui/binder';
-export { getFormat } from 'ui/visualize/loader/pipeline_helpers/utilities';
+export { getFormat, getTableAggs } from 'ui/visualize/loader/pipeline_helpers/utilities';
// @ts-ignore
export { tabifyAggResponse } from 'ui/agg_response/tabify';
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/line.ts b/src/legacy/core_plugins/vis_type_vislib/public/line.ts
index 35a059fadddcb..d3f2bc1bb684c 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/line.ts
+++ b/src/legacy/core_plugins/vis_type_vislib/public/line.ts
@@ -42,9 +42,9 @@ import { KbnVislibVisTypesDependencies } from './plugin';
export const createLineVisTypeDefinition = (deps: KbnVislibVisTypesDependencies) => ({
name: 'line',
- title: i18n.translate('kbnVislibVisTypes.line.lineTitle', { defaultMessage: 'Line' }),
+ title: i18n.translate('visTypeVislib.line.lineTitle', { defaultMessage: 'Line' }),
icon: 'visLine',
- description: i18n.translate('kbnVislibVisTypes.line.lineDescription', {
+ description: i18n.translate('visTypeVislib.line.lineDescription', {
defaultMessage: 'Emphasize trends',
}),
visualization: createVislibVisController(deps),
@@ -136,7 +136,7 @@ export const createLineVisTypeDefinition = (deps: KbnVislibVisTypesDependencies)
{
group: AggGroupNames.Metrics,
name: 'metric',
- title: i18n.translate('kbnVislibVisTypes.line.metricTitle', { defaultMessage: 'Y-axis' }),
+ title: i18n.translate('visTypeVislib.line.metricTitle', { defaultMessage: 'Y-axis' }),
min: 1,
aggFilter: ['!geo_centroid', '!geo_bounds'],
defaults: [{ schema: 'metric', type: 'count' }],
@@ -144,7 +144,7 @@ export const createLineVisTypeDefinition = (deps: KbnVislibVisTypesDependencies)
{
group: AggGroupNames.Metrics,
name: 'radius',
- title: i18n.translate('kbnVislibVisTypes.line.radiusTitle', { defaultMessage: 'Dot size' }),
+ title: i18n.translate('visTypeVislib.line.radiusTitle', { defaultMessage: 'Dot size' }),
min: 0,
max: 1,
aggFilter: ['count', 'avg', 'sum', 'min', 'max', 'cardinality', 'top_hits'],
@@ -152,7 +152,7 @@ export const createLineVisTypeDefinition = (deps: KbnVislibVisTypesDependencies)
{
group: AggGroupNames.Buckets,
name: 'segment',
- title: i18n.translate('kbnVislibVisTypes.line.segmentTitle', { defaultMessage: 'X-axis' }),
+ title: i18n.translate('visTypeVislib.line.segmentTitle', { defaultMessage: 'X-axis' }),
min: 0,
max: 1,
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
@@ -160,7 +160,7 @@ export const createLineVisTypeDefinition = (deps: KbnVislibVisTypesDependencies)
{
group: AggGroupNames.Buckets,
name: 'group',
- title: i18n.translate('kbnVislibVisTypes.line.groupTitle', {
+ title: i18n.translate('visTypeVislib.line.groupTitle', {
defaultMessage: 'Split series',
}),
min: 0,
@@ -170,7 +170,7 @@ export const createLineVisTypeDefinition = (deps: KbnVislibVisTypesDependencies)
{
group: AggGroupNames.Buckets,
name: 'split',
- title: i18n.translate('kbnVislibVisTypes.line.splitTitle', {
+ title: i18n.translate('visTypeVislib.line.splitTitle', {
defaultMessage: 'Split chart',
}),
min: 0,
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/pie.ts b/src/legacy/core_plugins/vis_type_vislib/public/pie.ts
index 32307b7a117a1..3c30030ca45f5 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/pie.ts
+++ b/src/legacy/core_plugins/vis_type_vislib/public/pie.ts
@@ -40,9 +40,9 @@ export interface PieVisParams extends CommonVislibParams {
export const createPieVisTypeDefinition = (deps: KbnVislibVisTypesDependencies) => ({
name: 'pie',
- title: i18n.translate('kbnVislibVisTypes.pie.pieTitle', { defaultMessage: 'Pie' }),
+ title: i18n.translate('visTypeVislib.pie.pieTitle', { defaultMessage: 'Pie' }),
icon: 'visPie',
- description: i18n.translate('kbnVislibVisTypes.pie.pieDescription', {
+ description: i18n.translate('visTypeVislib.pie.pieDescription', {
defaultMessage: 'Compare parts of a whole',
}),
visualization: createVislibVisController(deps),
@@ -70,7 +70,7 @@ export const createPieVisTypeDefinition = (deps: KbnVislibVisTypesDependencies)
{
group: AggGroupNames.Metrics,
name: 'metric',
- title: i18n.translate('kbnVislibVisTypes.pie.metricTitle', {
+ title: i18n.translate('visTypeVislib.pie.metricTitle', {
defaultMessage: 'Slice size',
}),
min: 1,
@@ -81,7 +81,7 @@ export const createPieVisTypeDefinition = (deps: KbnVislibVisTypesDependencies)
{
group: AggGroupNames.Buckets,
name: 'segment',
- title: i18n.translate('kbnVislibVisTypes.pie.segmentTitle', {
+ title: i18n.translate('visTypeVislib.pie.segmentTitle', {
defaultMessage: 'Split slices',
}),
min: 0,
@@ -91,7 +91,7 @@ export const createPieVisTypeDefinition = (deps: KbnVislibVisTypesDependencies)
{
group: AggGroupNames.Buckets,
name: 'split',
- title: i18n.translate('kbnVislibVisTypes.pie.splitTitle', {
+ title: i18n.translate('visTypeVislib.pie.splitTitle', {
defaultMessage: 'Split chart',
}),
mustBeFirst: true,
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/pie_fn.ts b/src/legacy/core_plugins/vis_type_vislib/public/pie_fn.ts
index 4b536caedb121..af9842fa94fda 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/pie_fn.ts
+++ b/src/legacy/core_plugins/vis_type_vislib/public/pie_fn.ts
@@ -53,7 +53,7 @@ export const createPieVisFn = (deps: KbnVislibVisTypesDependencies) => (): Expre
context: {
types: ['kibana_datatable'],
},
- help: i18n.translate('kbnVislibVisTypes.functions.pie.help', {
+ help: i18n.translate('visTypeVislib.functions.pie.help', {
defaultMessage: 'Pie visualization',
}),
args: {
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/plugin.ts b/src/legacy/core_plugins/vis_type_vislib/public/plugin.ts
index a2e8512b2201b..0ab2b2120382b 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/plugin.ts
+++ b/src/legacy/core_plugins/vis_type_vislib/public/plugin.ts
@@ -46,10 +46,6 @@ type ResponseHandlerProvider = () => {
type KbnVislibVisTypesCoreSetup = CoreSetup;
export interface LegacyDependencies {
- initializeHierarchicalTooltipFormatter: () => Promise;
- getHierarchicalTooltipFormatter: () => Promise;
- initializePointSeriesTooltipFormatter: () => void;
- getPointSeriesTooltipFormatter: () => void;
vislibSeriesResponseHandlerProvider: ResponseHandlerProvider;
vislibSlicesResponseHandlerProvider: ResponseHandlerProvider;
vislibColor: (colors: Array, mappings: any) => (value: any) => any;
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/utils/collections.ts b/src/legacy/core_plugins/vis_type_vislib/public/utils/collections.ts
index 810ddeea73834..d8f9fbf7d756a 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/utils/collections.ts
+++ b/src/legacy/core_plugins/vis_type_vislib/public/utils/collections.ts
@@ -31,25 +31,25 @@ export type Positions = $Values;
const getPositions = () => [
{
- text: i18n.translate('kbnVislibVisTypes.legendPositions.topText', {
+ text: i18n.translate('visTypeVislib.legendPositions.topText', {
defaultMessage: 'Top',
}),
value: Positions.TOP,
},
{
- text: i18n.translate('kbnVislibVisTypes.legendPositions.leftText', {
+ text: i18n.translate('visTypeVislib.legendPositions.leftText', {
defaultMessage: 'Left',
}),
value: Positions.LEFT,
},
{
- text: i18n.translate('kbnVislibVisTypes.legendPositions.rightText', {
+ text: i18n.translate('visTypeVislib.legendPositions.rightText', {
defaultMessage: 'Right',
}),
value: Positions.RIGHT,
},
{
- text: i18n.translate('kbnVislibVisTypes.legendPositions.bottomText', {
+ text: i18n.translate('visTypeVislib.legendPositions.bottomText', {
defaultMessage: 'Bottom',
}),
value: Positions.BOTTOM,
@@ -65,19 +65,19 @@ export type ChartTypes = $Values;
const getChartTypes = () => [
{
- text: i18n.translate('kbnVislibVisTypes.chartTypes.lineText', {
+ text: i18n.translate('visTypeVislib.chartTypes.lineText', {
defaultMessage: 'Line',
}),
value: ChartTypes.LINE,
},
{
- text: i18n.translate('kbnVislibVisTypes.chartTypes.areaText', {
+ text: i18n.translate('visTypeVislib.chartTypes.areaText', {
defaultMessage: 'Area',
}),
value: ChartTypes.AREA,
},
{
- text: i18n.translate('kbnVislibVisTypes.chartTypes.barText', {
+ text: i18n.translate('visTypeVislib.chartTypes.barText', {
defaultMessage: 'Bar',
}),
value: ChartTypes.HISTOGRAM,
@@ -92,13 +92,13 @@ export type ChartModes = $Values;
const getChartModes = () => [
{
- text: i18n.translate('kbnVislibVisTypes.chartModes.normalText', {
+ text: i18n.translate('visTypeVislib.chartModes.normalText', {
defaultMessage: 'Normal',
}),
value: ChartModes.NORMAL,
},
{
- text: i18n.translate('kbnVislibVisTypes.chartModes.stackedText', {
+ text: i18n.translate('visTypeVislib.chartModes.stackedText', {
defaultMessage: 'Stacked',
}),
value: ChartModes.STACKED,
@@ -114,19 +114,19 @@ export type InterpolationModes = $Values;
const getInterpolationModes = () => [
{
- text: i18n.translate('kbnVislibVisTypes.interpolationModes.straightText', {
+ text: i18n.translate('visTypeVislib.interpolationModes.straightText', {
defaultMessage: 'Straight',
}),
value: InterpolationModes.LINEAR,
},
{
- text: i18n.translate('kbnVislibVisTypes.interpolationModes.smoothedText', {
+ text: i18n.translate('visTypeVislib.interpolationModes.smoothedText', {
defaultMessage: 'Smoothed',
}),
value: InterpolationModes.CARDINAL,
},
{
- text: i18n.translate('kbnVislibVisTypes.interpolationModes.steppedText', {
+ text: i18n.translate('visTypeVislib.interpolationModes.steppedText', {
defaultMessage: 'Stepped',
}),
value: InterpolationModes.STEP_AFTER,
@@ -148,19 +148,19 @@ export type ScaleTypes = $Values;
const getScaleTypes = () => [
{
- text: i18n.translate('kbnVislibVisTypes.scaleTypes.linearText', {
+ text: i18n.translate('visTypeVislib.scaleTypes.linearText', {
defaultMessage: 'Linear',
}),
value: ScaleTypes.LINEAR,
},
{
- text: i18n.translate('kbnVislibVisTypes.scaleTypes.logText', {
+ text: i18n.translate('visTypeVislib.scaleTypes.logText', {
defaultMessage: 'Log',
}),
value: ScaleTypes.LOG,
},
{
- text: i18n.translate('kbnVislibVisTypes.scaleTypes.squareRootText', {
+ text: i18n.translate('visTypeVislib.scaleTypes.squareRootText', {
defaultMessage: 'Square root',
}),
value: ScaleTypes.SQUARE_ROOT,
@@ -177,25 +177,25 @@ export type AxisModes = $Values;
const getAxisModes = () => [
{
- text: i18n.translate('kbnVislibVisTypes.axisModes.normalText', {
+ text: i18n.translate('visTypeVislib.axisModes.normalText', {
defaultMessage: 'Normal',
}),
value: AxisModes.NORMAL,
},
{
- text: i18n.translate('kbnVislibVisTypes.axisModes.percentageText', {
+ text: i18n.translate('visTypeVislib.axisModes.percentageText', {
defaultMessage: 'Percentage',
}),
value: AxisModes.PERCENTAGE,
},
{
- text: i18n.translate('kbnVislibVisTypes.axisModes.wiggleText', {
+ text: i18n.translate('visTypeVislib.axisModes.wiggleText', {
defaultMessage: 'Wiggle',
}),
value: AxisModes.WIGGLE,
},
{
- text: i18n.translate('kbnVislibVisTypes.axisModes.silhouetteText', {
+ text: i18n.translate('visTypeVislib.axisModes.silhouetteText', {
defaultMessage: 'Silhouette',
}),
value: AxisModes.SILHOUETTE,
@@ -219,19 +219,19 @@ export type ThresholdLineStyles = $Values;
const getThresholdLineStyles = () => [
{
value: ThresholdLineStyles.FULL,
- text: i18n.translate('kbnVislibVisTypes.thresholdLine.style.fullText', {
+ text: i18n.translate('visTypeVislib.thresholdLine.style.fullText', {
defaultMessage: 'Full',
}),
},
{
value: ThresholdLineStyles.DASHED,
- text: i18n.translate('kbnVislibVisTypes.thresholdLine.style.dashedText', {
+ text: i18n.translate('visTypeVislib.thresholdLine.style.dashedText', {
defaultMessage: 'Dashed',
}),
},
{
value: ThresholdLineStyles.DOT_DASHED,
- text: i18n.translate('kbnVislibVisTypes.thresholdLine.style.dotdashedText', {
+ text: i18n.translate('visTypeVislib.thresholdLine.style.dotdashedText', {
defaultMessage: 'Dot-dashed',
}),
},
@@ -239,19 +239,19 @@ const getThresholdLineStyles = () => [
const getRotateOptions = () => [
{
- text: i18n.translate('kbnVislibVisTypes.categoryAxis.rotate.horizontalText', {
+ text: i18n.translate('visTypeVislib.categoryAxis.rotate.horizontalText', {
defaultMessage: 'Horizontal',
}),
value: Rotates.HORIZONTAL,
},
{
- text: i18n.translate('kbnVislibVisTypes.categoryAxis.rotate.verticalText', {
+ text: i18n.translate('visTypeVislib.categoryAxis.rotate.verticalText', {
defaultMessage: 'Vertical',
}),
value: Rotates.VERTICAL,
},
{
- text: i18n.translate('kbnVislibVisTypes.categoryAxis.rotate.angledText', {
+ text: i18n.translate('visTypeVislib.categoryAxis.rotate.angledText', {
defaultMessage: 'Angled',
}),
value: Rotates.ANGLED,
@@ -273,13 +273,13 @@ export type ColorModes = $Values;
const getGaugeTypes = () => [
{
- text: i18n.translate('kbnVislibVisTypes.gauge.gaugeTypes.arcText', {
+ text: i18n.translate('visTypeVislib.gauge.gaugeTypes.arcText', {
defaultMessage: 'Arc',
}),
value: GaugeTypes.ARC,
},
{
- text: i18n.translate('kbnVislibVisTypes.gauge.gaugeTypes.circleText', {
+ text: i18n.translate('visTypeVislib.gauge.gaugeTypes.circleText', {
defaultMessage: 'Circle',
}),
value: GaugeTypes.CIRCLE,
@@ -295,19 +295,19 @@ export type Alignments = $Values;
const getAlignments = () => [
{
- text: i18n.translate('kbnVislibVisTypes.gauge.alignmentAutomaticTitle', {
+ text: i18n.translate('visTypeVislib.gauge.alignmentAutomaticTitle', {
defaultMessage: 'Automatic',
}),
value: Alignments.AUTOMATIC,
},
{
- text: i18n.translate('kbnVislibVisTypes.gauge.alignmentHorizontalTitle', {
+ text: i18n.translate('visTypeVislib.gauge.alignmentHorizontalTitle', {
defaultMessage: 'Horizontal',
}),
value: Alignments.HORIZONTAL,
},
{
- text: i18n.translate('kbnVislibVisTypes.gauge.alignmentVerticalTitle', {
+ text: i18n.translate('visTypeVislib.gauge.alignmentVerticalTitle', {
defaultMessage: 'Vertical',
}),
value: Alignments.VERTICAL,
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/utils/common_config.tsx b/src/legacy/core_plugins/vis_type_vislib/public/utils/common_config.tsx
index adb93ca8011b2..e2f6ba0d8b562 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/utils/common_config.tsx
+++ b/src/legacy/core_plugins/vis_type_vislib/public/utils/common_config.tsx
@@ -29,7 +29,7 @@ function getAreaOptionTabs() {
return [
{
name: 'advanced',
- title: i18n.translate('kbnVislibVisTypes.area.tabs.metricsAxesTitle', {
+ title: i18n.translate('visTypeVislib.area.tabs.metricsAxesTitle', {
defaultMessage: 'Metrics & axes',
}),
editor: (props: VisOptionsProps) => (
@@ -38,7 +38,7 @@ function getAreaOptionTabs() {
},
{
name: 'options',
- title: i18n.translate('kbnVislibVisTypes.area.tabs.panelSettingsTitle', {
+ title: i18n.translate('visTypeVislib.area.tabs.panelSettingsTitle', {
defaultMessage: 'Panel settings',
}),
editor: (props: VisOptionsProps) => (
@@ -48,7 +48,7 @@ function getAreaOptionTabs() {
];
}
-const countLabel = i18n.translate('kbnVislibVisTypes.area.countText', {
+const countLabel = i18n.translate('visTypeVislib.area.countText', {
defaultMessage: 'Count',
});
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vis_controller.tsx b/src/legacy/core_plugins/vis_type_vislib/public/vis_controller.tsx
index cff9a0a2e8551..ca49f17e7c264 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vis_controller.tsx
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vis_controller.tsx
@@ -20,12 +20,13 @@
import $ from 'jquery';
import React, { RefObject } from 'react';
-import { CUSTOM_LEGEND_VIS_TYPES, VisLegend, Vis, VisParams } from './legacy_imports';
+import { Vis, VisParams } from './legacy_imports';
// @ts-ignore
import { Vis as Vislib } from './vislib/vis';
import { Positions } from './utils/collections';
import { KbnVislibVisTypesDependencies } from './plugin';
import { mountReactNode } from '../../../../core/public/utils';
+import { VisLegend, CUSTOM_LEGEND_VIS_TYPES } from './vislib/components/legend';
const legendClassName = {
top: 'visLib--legend-top',
@@ -76,9 +77,6 @@ export const createVislibVisController = (deps: KbnVislibVisTypesDependencies) =
return resolve();
}
- await deps.initializeHierarchicalTooltipFormatter();
- await deps.initializePointSeriesTooltipFormatter();
-
this.vislibVis = new Vislib(this.chartEl, visParams, deps);
this.vislibVis.on('brush', this.vis.API.events.brush);
this.vislibVis.on('click', this.vis.API.events.filter);
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts b/src/legacy/core_plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts
index 0a685cd70e089..8a35fe3a0f6f8 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts
@@ -52,7 +52,7 @@ export const createKbnVislibVisTypesFn = (
context: {
types: ['kibana_datatable'],
},
- help: i18n.translate('kbnVislibVisTypes.functions.vislib.help', {
+ help: i18n.translate('visTypeVislib.functions.vislib.help', {
defaultMessage: 'Vislib visualization',
}),
args: {
diff --git a/src/legacy/ui/public/vis/components/tooltip/__tests__/_tooltip_formatter.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/_tooltip_formatter.js
similarity index 88%
rename from src/legacy/ui/public/vis/components/tooltip/__tests__/_tooltip_formatter.js
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/_tooltip_formatter.js
index d0cfab0b54f79..a3aabcb90be62 100644
--- a/src/legacy/ui/public/vis/components/tooltip/__tests__/_tooltip_formatter.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/_tooltip_formatter.js
@@ -20,18 +20,11 @@
import _ from 'lodash';
import $ from 'jquery';
import expect from '@kbn/expect';
-import ngMock from 'ng_mock';
-import { PointSeriesTooltipFormatterProvider } from '../_pointseries_tooltip_formatter';
-describe('tooltipFormatter', function() {
- let tooltipFormatter;
+import { pointSeriesTooltipFormatter } from '../../components/tooltip';
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function(Private) {
- tooltipFormatter = Private(PointSeriesTooltipFormatterProvider)();
- })
- );
+describe('tooltipFormatter', function() {
+ const tooltipFormatter = pointSeriesTooltipFormatter();
function cell($row, i) {
return $row
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/heatmap_color.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/heatmap_color.js
index 36c5b60abf5c6..c7ffc843876e3 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/heatmap_color.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/heatmap_color.js
@@ -18,7 +18,6 @@
*/
import expect from '@kbn/expect';
-import ngMock from 'ng_mock';
import { getHeatmapColors } from '../../../legacy_imports';
@@ -27,8 +26,6 @@ describe('Vislib Heatmap Color Module Test Suite', function() {
const nullValue = null;
let notAValue;
- beforeEach(ngMock.module('kibana'));
-
it('should throw an error if schema is invalid', function() {
expect(function() {
getHeatmapColors(4, 'invalid schema');
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/labels.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/labels.js
index 55d629adaf245..db99b881a6e38 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/labels.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/labels.js
@@ -19,7 +19,7 @@
import _ from 'lodash';
import expect from '@kbn/expect';
-import ngMock from 'ng_mock';
+
import { labels } from '../../components/labels/labels';
import { dataArray } from '../../components/labels/data_array';
import { uniqLabels } from '../../components/labels/uniq_labels';
@@ -161,21 +161,18 @@ const columnsData = {
describe('Vislib Labels Module Test Suite', function() {
let uniqSeriesLabels;
describe('Labels (main)', function() {
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function() {
- seriesLabels = labels(seriesData);
- rowsLabels = labels(rowsData);
- seriesArr = Array.isArray(seriesLabels);
- rowsArr = Array.isArray(rowsLabels);
- uniqSeriesLabels = _.chain(rowsData.rows)
- .pluck('series')
- .flattenDeep()
- .pluck('label')
- .uniq()
- .value();
- })
- );
+ beforeEach(() => {
+ seriesLabels = labels(seriesData);
+ rowsLabels = labels(rowsData);
+ seriesArr = Array.isArray(seriesLabels);
+ rowsArr = Array.isArray(rowsLabels);
+ uniqSeriesLabels = _.chain(rowsData.rows)
+ .pluck('series')
+ .flattenDeep()
+ .pluck('label')
+ .uniq()
+ .value();
+ });
it('should be a function', function() {
expect(typeof labels).to.be('function');
@@ -224,15 +221,12 @@ describe('Vislib Labels Module Test Suite', function() {
let testSeries;
let testRows;
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function() {
- seriesLabels = dataArray(seriesData);
- rowsLabels = dataArray(rowsData);
- testSeries = Array.isArray(seriesLabels);
- testRows = Array.isArray(rowsLabels);
- })
- );
+ beforeEach(() => {
+ seriesLabels = dataArray(seriesData);
+ rowsLabels = dataArray(rowsData);
+ testSeries = Array.isArray(seriesLabels);
+ testRows = Array.isArray(rowsLabels);
+ });
it('should throw an error if the input is not an object', function() {
expect(function() {
@@ -333,15 +327,12 @@ describe('Vislib Labels Module Test Suite', function() {
let uniq;
let testArr;
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function() {
- uniq = uniqLabels(arrObj, function(d) {
- return d;
- });
- testArr = Array.isArray(uniq);
- })
- );
+ beforeEach(() => {
+ uniq = uniqLabels(arrObj, function(d) {
+ return d;
+ });
+ testArr = Array.isArray(uniq);
+ });
it('should throw an error if input is not an array', function() {
expect(function() {
@@ -407,15 +398,12 @@ describe('Vislib Labels Module Test Suite', function() {
let columnsArr;
let rowsArr;
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function() {
- columnsLabels = getSeries(columnsData);
- rowsLabels = getSeries(rowsData);
- columnsArr = Array.isArray(columnsLabels);
- rowsArr = Array.isArray(rowsLabels);
- })
- );
+ beforeEach(() => {
+ columnsLabels = getSeries(columnsData);
+ rowsLabels = getSeries(rowsData);
+ columnsArr = Array.isArray(columnsLabels);
+ rowsArr = Array.isArray(rowsLabels);
+ });
it('should throw an error if input is not an object', function() {
expect(function() {
diff --git a/src/legacy/ui/public/vis/components/tooltip/__tests__/positioning.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/positioning.js
similarity index 99%
rename from src/legacy/ui/public/vis/components/tooltip/__tests__/positioning.js
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/positioning.js
index 89d547f2377ba..f1c80c9981020 100644
--- a/src/legacy/ui/public/vis/components/tooltip/__tests__/positioning.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/positioning.js
@@ -21,7 +21,8 @@ import expect from '@kbn/expect';
import $ from 'jquery';
import _ from 'lodash';
import sinon from 'sinon';
-import { positionTooltip } from '../position_tooltip';
+
+import { positionTooltip } from '../../components/tooltip/position_tooltip';
describe('Tooltip Positioning', function() {
const sandbox = sinon.createSandbox();
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/index.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/index.js
index e30ca17a1fce7..734c6d003278f 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/index.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/index.js
@@ -19,18 +19,15 @@
import _ from 'lodash';
import expect from '@kbn/expect';
-import ngMock from 'ng_mock';
+
import VislibProvider from '..';
describe('Vislib Index Test Suite', function() {
let vislib;
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function(Private) {
- vislib = Private(VislibProvider);
- })
- );
+ beforeEach(() => {
+ vislib = new VislibProvider();
+ });
it('should return an object', function() {
expect(_.isObject(vislib)).to.be(true);
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis/axis.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis/axis.js
index 3081c12415076..bc4a4f9925513 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis/axis.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis/axis.js
@@ -19,17 +19,15 @@
import d3 from 'd3';
import _ from 'lodash';
-import ngMock from 'ng_mock';
-import 'ui/persisted_state';
-
import expect from '@kbn/expect';
import $ from 'jquery';
import { Axis } from '../../../lib/axis';
import { VisConfig } from '../../../lib/vis_config';
+import { getMockUiState } from '../fixtures/_vis_fixture';
describe('Vislib Axis Class Test Suite', function() {
- let persistedState;
+ let mockUiState;
let yAxis;
let el;
let fixture;
@@ -102,38 +100,34 @@ describe('Vislib Axis Class Test Suite', function() {
yAxisLabel: 'Count',
};
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function($injector) {
- persistedState = new ($injector.get('PersistedState'))();
-
- el = d3
- .select('body')
- .append('div')
- .attr('class', 'visAxis--x')
- .style('height', '40px');
+ beforeEach(() => {
+ mockUiState = getMockUiState();
+ el = d3
+ .select('body')
+ .append('div')
+ .attr('class', 'visAxis--x')
+ .style('height', '40px');
- fixture = el.append('div').attr('class', 'x-axis-div');
+ fixture = el.append('div').attr('class', 'x-axis-div');
- const visConfig = new VisConfig(
- {
- type: 'histogram',
- },
- data,
- persistedState,
- $('.x-axis-div')[0],
- () => undefined
- );
- yAxis = new Axis(visConfig, {
- type: 'value',
- id: 'ValueAxis-1',
- });
+ const visConfig = new VisConfig(
+ {
+ type: 'histogram',
+ },
+ data,
+ mockUiState,
+ $('.x-axis-div')[0],
+ () => undefined
+ );
+ yAxis = new Axis(visConfig, {
+ type: 'value',
+ id: 'ValueAxis-1',
+ });
- seriesData = data.series.map(series => {
- return series.values;
- });
- })
- );
+ seriesData = data.series.map(series => {
+ return series.values;
+ });
+ });
afterEach(function() {
fixture.remove();
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis_title.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis_title.js
index cbb294c3b44e4..fd25335dd2cd4 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis_title.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis_title.js
@@ -20,18 +20,15 @@
import d3 from 'd3';
import _ from 'lodash';
import $ from 'jquery';
-import ngMock from 'ng_mock';
-
import expect from '@kbn/expect';
-import 'ui/persisted_state';
import { AxisTitle } from '../../lib/axis/axis_title';
import { AxisConfig } from '../../lib/axis/axis_config';
import { VisConfig } from '../../lib/vis_config';
import { Data } from '../../lib/data';
+import { getMockUiState } from './fixtures/_vis_fixture';
describe('Vislib AxisTitle Class Test Suite', function() {
- let PersistedState;
let el;
let dataObj;
let xTitle;
@@ -96,56 +93,53 @@ describe('Vislib AxisTitle Class Test Suite', function() {
yAxisLabel: 'Count',
};
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function($injector) {
- PersistedState = $injector.get('PersistedState');
-
- el = d3
- .select('body')
- .append('div')
- .attr('class', 'visWrapper');
-
- el.append('div')
- .attr('class', 'visAxis__column--bottom')
- .append('div')
- .attr('class', 'axis-title y-axis-title')
- .style('height', '20px')
- .style('width', '20px');
-
- el.append('div')
- .attr('class', 'visAxis__column--left')
- .append('div')
- .attr('class', 'axis-title x-axis-title')
- .style('height', '20px')
- .style('width', '20px');
-
- dataObj = new Data(data, new PersistedState(), () => undefined);
- visConfig = new VisConfig(
- {
- type: 'histogram',
- },
- data,
- new PersistedState(),
- el.node(),
- () => undefined
- );
- const xAxisConfig = new AxisConfig(visConfig, {
- position: 'bottom',
- title: {
- text: dataObj.get('xAxisLabel'),
- },
- });
- const yAxisConfig = new AxisConfig(visConfig, {
- position: 'left',
- title: {
- text: dataObj.get('yAxisLabel'),
- },
- });
- xTitle = new AxisTitle(xAxisConfig);
- yTitle = new AxisTitle(yAxisConfig);
- })
- );
+ beforeEach(() => {
+ el = d3
+ .select('body')
+ .append('div')
+ .attr('class', 'visWrapper');
+
+ el.append('div')
+ .attr('class', 'visAxis__column--bottom')
+ .append('div')
+ .attr('class', 'axis-title y-axis-title')
+ .style('height', '20px')
+ .style('width', '20px');
+
+ el.append('div')
+ .attr('class', 'visAxis__column--left')
+ .append('div')
+ .attr('class', 'axis-title x-axis-title')
+ .style('height', '20px')
+ .style('width', '20px');
+
+ const uiState = getMockUiState();
+ uiState.set('vis.colors', []);
+ dataObj = new Data(data, getMockUiState(), () => undefined);
+ visConfig = new VisConfig(
+ {
+ type: 'histogram',
+ },
+ data,
+ getMockUiState(),
+ el.node(),
+ () => undefined
+ );
+ const xAxisConfig = new AxisConfig(visConfig, {
+ position: 'bottom',
+ title: {
+ text: dataObj.get('xAxisLabel'),
+ },
+ });
+ const yAxisConfig = new AxisConfig(visConfig, {
+ position: 'left',
+ title: {
+ text: dataObj.get('yAxisLabel'),
+ },
+ });
+ xTitle = new AxisTitle(xAxisConfig);
+ yTitle = new AxisTitle(yAxisConfig);
+ });
afterEach(function() {
el.remove();
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/chart_title.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/chart_title.js
index b2086d0749a41..b65571becd83c 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/chart_title.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/chart_title.js
@@ -19,14 +19,14 @@
import d3 from 'd3';
import _ from 'lodash';
-import ngMock from 'ng_mock';
import expect from '@kbn/expect';
import { ChartTitle } from '../../lib/chart_title';
import { VisConfig } from '../../lib/vis_config';
+import { getMockUiState } from './fixtures/_vis_fixture';
describe('Vislib ChartTitle Class Test Suite', function() {
- let persistedState;
+ let mockUiState;
let chartTitle;
let el;
const data = {
@@ -88,36 +88,32 @@ describe('Vislib ChartTitle Class Test Suite', function() {
yAxisLabel: 'Count',
};
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function($injector) {
- persistedState = new ($injector.get('PersistedState'))();
+ beforeEach(() => {
+ mockUiState = getMockUiState();
+ el = d3
+ .select('body')
+ .append('div')
+ .attr('class', 'visWrapper')
+ .datum(data);
- el = d3
- .select('body')
- .append('div')
- .attr('class', 'visWrapper')
- .datum(data);
+ el.append('div')
+ .attr('class', 'chart-title')
+ .style('height', '20px');
- el.append('div')
- .attr('class', 'chart-title')
- .style('height', '20px');
-
- const visConfig = new VisConfig(
- {
- type: 'histogram',
- title: {
- text: 'rows',
- },
+ const visConfig = new VisConfig(
+ {
+ type: 'histogram',
+ title: {
+ text: 'rows',
},
- data,
- persistedState,
- el.node(),
- () => undefined
- );
- chartTitle = new ChartTitle(visConfig);
- })
- );
+ },
+ data,
+ mockUiState,
+ el.node(),
+ () => undefined
+ );
+ chartTitle = new ChartTitle(visConfig);
+ });
afterEach(function() {
el.remove();
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/data.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/data.js
index 5811b1d238163..d4ec6f363a75b 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/data.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/data.js
@@ -18,11 +18,10 @@
*/
import _ from 'lodash';
-import ngMock from 'ng_mock';
import expect from '@kbn/expect';
-import 'ui/persisted_state';
import { Data } from '../../lib/data';
+import { getMockUiState } from './fixtures/_vis_fixture';
const seriesData = {
label: '',
@@ -153,14 +152,11 @@ const colsData = {
};
describe('Vislib Data Class Test Suite', function() {
- let persistedState;
+ let mockUiState;
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function($injector) {
- persistedState = new ($injector.get('PersistedState'))();
- })
- );
+ beforeEach(() => {
+ mockUiState = getMockUiState();
+ });
describe('Data Class (main)', function() {
it('should be a function', function() {
@@ -168,7 +164,7 @@ describe('Vislib Data Class Test Suite', function() {
});
it('should return an object', function() {
- const rowIn = new Data(rowsData, persistedState, () => undefined);
+ const rowIn = new Data(rowsData, mockUiState, () => undefined);
expect(_.isObject(rowIn)).to.be(true);
});
});
@@ -182,7 +178,7 @@ describe('Vislib Data Class Test Suite', function() {
};
beforeEach(function() {
- data = new Data(pieData, persistedState, () => undefined);
+ data = new Data(pieData, mockUiState, () => undefined);
});
it('should remove zero values', function() {
@@ -196,7 +192,7 @@ describe('Vislib Data Class Test Suite', function() {
let serOut;
beforeEach(function() {
- serIn = new Data(seriesData, persistedState, () => undefined);
+ serIn = new Data(seriesData, mockUiState, () => undefined);
serOut = serIn.flatten();
});
@@ -210,7 +206,7 @@ describe('Vislib Data Class Test Suite', function() {
function testLength(inputData) {
return function() {
- const data = new Data(inputData, persistedState, () => undefined);
+ const data = new Data(inputData, mockUiState, () => undefined);
const len = _.reduce(
data.chartData(),
function(sum, chart) {
@@ -266,7 +262,7 @@ describe('Vislib Data Class Test Suite', function() {
};
beforeEach(function() {
- data = new Data(geohashGridData, persistedState, () => undefined);
+ data = new Data(geohashGridData, mockUiState, () => undefined);
});
describe('getVisData', function() {
@@ -287,7 +283,7 @@ describe('Vislib Data Class Test Suite', function() {
describe('null value check', function() {
it('should return false', function() {
- const data = new Data(rowsData, persistedState, () => undefined);
+ const data = new Data(rowsData, mockUiState, () => undefined);
expect(data.hasNullValues()).to.be(false);
});
@@ -307,7 +303,7 @@ describe('Vislib Data Class Test Suite', function() {
],
});
- const data = new Data(nullRowData, persistedState, () => undefined);
+ const data = new Data(nullRowData, mockUiState, () => undefined);
expect(data.hasNullValues()).to.be(true);
});
});
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch.js
index a93db5637c89d..760c2e80d8428 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch.js
@@ -19,14 +19,12 @@
import _ from 'lodash';
import d3 from 'd3';
-import ngMock from 'ng_mock';
-
import expect from '@kbn/expect';
-import 'ui/persisted_state';
// Data
import data from './fixtures/mock_data/date_histogram/_series';
-import getFixturesVislibVisFixtureProvider from './fixtures/_vis_fixture';
+
+import { getVis, getMockUiState } from './fixtures/_vis_fixture';
import { SimpleEmitter } from '../../../legacy_imports';
describe('Vislib Dispatch Class Test Suite', function() {
@@ -44,17 +42,13 @@ describe('Vislib Dispatch Class Test Suite', function() {
describe('', function() {
let vis;
- let persistedState;
-
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function(Private, $injector) {
- const getVis = getFixturesVislibVisFixtureProvider(Private);
- vis = getVis();
- persistedState = new ($injector.get('PersistedState'))();
- vis.render(data, persistedState);
- })
- );
+ let mockUiState;
+
+ beforeEach(() => {
+ vis = getVis();
+ mockUiState = getMockUiState();
+ vis.render(data, mockUiState);
+ });
afterEach(function() {
destroyVis(vis);
@@ -71,18 +65,14 @@ describe('Vislib Dispatch Class Test Suite', function() {
describe('Stock event handlers', function() {
let vis;
- let persistedState;
-
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function(Private, $injector) {
- const getVis = getFixturesVislibVisFixtureProvider(Private);
- persistedState = new ($injector.get('PersistedState'))();
- vis = getVis();
- vis.on('brush', _.noop);
- vis.render(data, persistedState);
- })
- );
+ let mockUiState;
+
+ beforeEach(() => {
+ mockUiState = getMockUiState();
+ vis = getVis();
+ vis.on('brush', _.noop);
+ vis.render(data, mockUiState);
+ });
afterEach(function() {
destroyVis(vis);
@@ -187,42 +177,30 @@ describe('Vislib Dispatch Class Test Suite', function() {
describe('Custom event handlers', function() {
it('should attach whatever gets passed on vis.on() to chart.events', function(done) {
- let vis;
- let persistedState;
- ngMock.module('kibana');
- ngMock.inject(function(Private, $injector) {
- const getVis = getFixturesVislibVisFixtureProvider(Private);
- vis = getVis();
- persistedState = new ($injector.get('PersistedState'))();
- vis.on('someEvent', _.noop);
- vis.render(data, persistedState);
-
- vis.handler.charts.forEach(function(chart) {
- expect(chart.events.listenerCount('someEvent')).to.be(1);
- });
+ const vis = getVis();
+ const mockUiState = getMockUiState();
+ vis.on('someEvent', _.noop);
+ vis.render(data, mockUiState);
- destroyVis(vis);
- done();
+ vis.handler.charts.forEach(function(chart) {
+ expect(chart.events.listenerCount('someEvent')).to.be(1);
});
+
+ destroyVis(vis);
+ done();
});
it('can be added after rendering', function() {
- let vis;
- let persistedState;
- ngMock.module('kibana');
- ngMock.inject(function(Private, $injector) {
- const getVis = getFixturesVislibVisFixtureProvider(Private);
- vis = getVis();
- persistedState = new ($injector.get('PersistedState'))();
- vis.render(data, persistedState);
- vis.on('someEvent', _.noop);
-
- vis.handler.charts.forEach(function(chart) {
- expect(chart.events.listenerCount('someEvent')).to.be(1);
- });
+ const vis = getVis();
+ const mockUiState = getMockUiState();
+ vis.render(data, mockUiState);
+ vis.on('someEvent', _.noop);
- destroyVis(vis);
+ vis.handler.charts.forEach(function(chart) {
+ expect(chart.events.listenerCount('someEvent')).to.be(1);
});
+
+ destroyVis(vis);
});
});
});
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/error_handler.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/error_handler.js
index 244386c1b51e6..4523e70ccbb4c 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/error_handler.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/error_handler.js
@@ -18,18 +18,15 @@
*/
import expect from '@kbn/expect';
-import ngMock from 'ng_mock';
+
import { ErrorHandler } from '../../lib/_error_handler';
describe('Vislib ErrorHandler Test Suite', function() {
let errorHandler;
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function() {
- errorHandler = new ErrorHandler();
- })
- );
+ beforeEach(() => {
+ errorHandler = new ErrorHandler();
+ });
describe('validateWidthandHeight Method', function() {
it('should throw an error when width and/or height is 0', function() {
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/_vis_fixture.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/_vis_fixture.js
index c49ca732f0915..c001a04f4b6e8 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/_vis_fixture.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/_vis_fixture.js
@@ -22,15 +22,6 @@ import $ from 'jquery';
import { Vis } from '../../../vis';
-// TODO: remove legacy imports when/of converting tests to jest
-import {
- setHierarchicalTooltipFormatter,
- getHierarchicalTooltipFormatter,
-} from 'ui/vis/components/tooltip/_hierarchical_tooltip_formatter';
-import {
- setPointSeriesTooltipFormatter,
- getPointSeriesTooltipFormatter,
-} from 'ui/vis/components/tooltip/_pointseries_tooltip_formatter';
import {
vislibSeriesResponseHandlerProvider,
vislibSlicesResponseHandlerProvider,
@@ -73,29 +64,33 @@ const getDeps = () => {
return {
uiSettings,
vislibColor,
- getHierarchicalTooltipFormatter,
- getPointSeriesTooltipFormatter,
vislibSeriesResponseHandlerProvider,
vislibSlicesResponseHandlerProvider,
};
};
-export default function getVislibFixtures(Private) {
- setHierarchicalTooltipFormatter(Private);
- setPointSeriesTooltipFormatter(Private);
+export const getMockUiState = () => {
+ const map = new Map();
- return function(visLibParams, element) {
- return new Vis(
- element || $visCanvas.new(),
- _.defaults({}, visLibParams || {}, {
- addTooltip: true,
- addLegend: true,
- defaultYExtents: false,
- setYExtents: false,
- yAxis: {},
- type: 'histogram',
- }),
- getDeps()
- );
- };
+ return (() => ({
+ get: (...args) => map.get(...args),
+ set: (...args) => map.set(...args),
+ setSilent: (...args) => map.set(...args),
+ on: () => undefined,
+ }))();
+};
+
+export function getVis(visLibParams, element) {
+ return new Vis(
+ element || $visCanvas.new(),
+ _.defaults({}, visLibParams || {}, {
+ addTooltip: true,
+ addLegend: true,
+ defaultYExtents: false,
+ setYExtents: false,
+ yAxis: {},
+ type: 'histogram',
+ }),
+ getDeps()
+ );
}
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/handler/handler.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/handler/handler.js
index b309c97d24000..8e25015c10186 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/handler/handler.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/handler/handler.js
@@ -17,36 +17,29 @@
* under the License.
*/
-import ngMock from 'ng_mock';
import expect from '@kbn/expect';
+import $ from 'jquery';
// Data
import series from '../fixtures/mock_data/date_histogram/_series';
import columns from '../fixtures/mock_data/date_histogram/_columns';
import rows from '../fixtures/mock_data/date_histogram/_rows';
import stackedSeries from '../fixtures/mock_data/date_histogram/_stacked_series';
-import $ from 'jquery';
-import 'ui/persisted_state';
-import getFixturesVislibVisFixtureProvider from '../fixtures/_vis_fixture';
+import { getVis, getMockUiState } from '../fixtures/_vis_fixture';
+
const dateHistogramArray = [series, columns, rows, stackedSeries];
const names = ['series', 'columns', 'rows', 'stackedSeries'];
dateHistogramArray.forEach(function(data, i) {
describe('Vislib Handler Test Suite for ' + names[i] + ' Data', function() {
- let vis;
- let persistedState;
const events = ['click', 'brush'];
+ let vis;
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function(Private, $injector) {
- const getVis = getFixturesVislibVisFixtureProvider(Private);
- vis = getVis();
- persistedState = new ($injector.get('PersistedState'))();
- vis.render(data, persistedState);
- })
- );
+ beforeEach(() => {
+ vis = getVis();
+ vis.render(data, getMockUiState());
+ });
afterEach(function() {
vis.destroy();
@@ -107,9 +100,7 @@ dateHistogramArray.forEach(function(data, i) {
describe('removeAll Method', function() {
beforeEach(function() {
- ngMock.inject(function() {
- vis.handler.removeAll(vis.element);
- });
+ vis.handler.removeAll(vis.element);
});
it('should remove all DOM elements from the el', function() {
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout.js
index c8636f34ce6f8..f72794e27e834 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout.js
@@ -18,18 +18,17 @@
*/
import d3 from 'd3';
-import ngMock from 'ng_mock';
import expect from '@kbn/expect';
-import 'ui/persisted_state';
+import $ from 'jquery';
// Data
import series from '../fixtures/mock_data/date_histogram/_series';
import columns from '../fixtures/mock_data/date_histogram/_columns';
import rows from '../fixtures/mock_data/date_histogram/_rows';
import stackedSeries from '../fixtures/mock_data/date_histogram/_stacked_series';
-import $ from 'jquery';
+
import { Layout } from '../../../lib/layout/layout';
-import getFixturesVislibVisFixtureProvider from '../fixtures/_vis_fixture';
+import { getVis, getMockUiState } from '../fixtures/_vis_fixture';
import { VisConfig } from '../../../lib/vis_config';
const dateHistogramArray = [series, columns, rows, stackedSeries];
@@ -38,23 +37,18 @@ const names = ['series', 'columns', 'rows', 'stackedSeries'];
dateHistogramArray.forEach(function(data, i) {
describe('Vislib Layout Class Test Suite for ' + names[i] + ' Data', function() {
let vis;
- let persistedState;
+ let mockUiState;
let numberOfCharts;
let testLayout;
- beforeEach(ngMock.module('kibana'));
-
- beforeEach(function() {
- ngMock.inject(function(Private, $injector) {
- const getVis = getFixturesVislibVisFixtureProvider(Private);
- vis = getVis();
- persistedState = new ($injector.get('PersistedState'))();
- vis.render(data, persistedState);
- numberOfCharts = vis.handler.charts.length;
- });
+ beforeEach(() => {
+ vis = getVis();
+ mockUiState = getMockUiState();
+ vis.render(data, mockUiState);
+ numberOfCharts = vis.handler.charts.length;
});
- afterEach(function() {
+ afterEach(() => {
vis.destroy();
});
@@ -81,7 +75,7 @@ dateHistogramArray.forEach(function(data, i) {
type: 'histogram',
},
data,
- persistedState,
+ mockUiState,
vis.element,
() => undefined
);
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout_types.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout_types.js
index 4dc52309c76fd..cc6d33a2d98da 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout_types.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout_types.js
@@ -18,19 +18,16 @@
*/
import _ from 'lodash';
-import ngMock from 'ng_mock';
import expect from '@kbn/expect';
+
import { layoutTypes as layoutType } from '../../../lib/layout/layout_types';
describe('Vislib Layout Types Test Suite', function() {
let layoutFunc;
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function() {
- layoutFunc = layoutType.point_series;
- })
- );
+ beforeEach(() => {
+ layoutFunc = layoutType.point_series;
+ });
it('should be an object', function() {
expect(_.isObject(layoutType)).to.be(true);
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/column_chart/splits.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/column_chart/splits.js
index 2626fd3ace2b0..3942aa18891b8 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/column_chart/splits.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/column_chart/splits.js
@@ -18,9 +18,9 @@
*/
import d3 from 'd3';
-import ngMock from 'ng_mock';
import expect from '@kbn/expect';
import $ from 'jquery';
+
import { chartSplit } from '../../../../../lib/layout/splits/column_chart/chart_split';
import { chartTitleSplit } from '../../../../../lib/layout/splits/column_chart/chart_title_split';
import { xAxisSplit } from '../../../../../lib/layout/splits/column_chart/x_axis_split';
@@ -150,16 +150,13 @@ describe('Vislib Split Function Test Suite', function() {
],
};
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function() {
- el = d3
- .select('body')
- .append('div')
- .attr('class', 'visualization')
- .datum(data);
- })
- );
+ beforeEach(() => {
+ el = d3
+ .select('body')
+ .append('div')
+ .attr('class', 'visualization')
+ .datum(data);
+ });
afterEach(function() {
el.remove();
@@ -168,11 +165,9 @@ describe('Vislib Split Function Test Suite', function() {
describe('chart split function', function() {
let fixture;
- beforeEach(
- ngMock.inject(function() {
- fixture = d3.select('.visualization').call(chartSplit);
- })
- );
+ beforeEach(function() {
+ fixture = d3.select('.visualization').call(chartSplit);
+ });
afterEach(function() {
fixture.remove();
@@ -192,28 +187,26 @@ describe('Vislib Split Function Test Suite', function() {
let newEl;
let fixture;
- beforeEach(
- ngMock.inject(function() {
- visEl = el.append('div').attr('class', 'visWrapper');
- visEl.append('div').attr('class', 'visAxis__splitTitles--x');
- visEl.append('div').attr('class', 'visAxis__splitTitles--y');
- visEl.select('.visAxis__splitTitles--x').call(chartTitleSplit);
- visEl.select('.visAxis__splitTitles--y').call(chartTitleSplit);
+ beforeEach(function() {
+ visEl = el.append('div').attr('class', 'visWrapper');
+ visEl.append('div').attr('class', 'visAxis__splitTitles--x');
+ visEl.append('div').attr('class', 'visAxis__splitTitles--y');
+ visEl.select('.visAxis__splitTitles--x').call(chartTitleSplit);
+ visEl.select('.visAxis__splitTitles--y').call(chartTitleSplit);
- newEl = d3
- .select('body')
- .append('div')
- .attr('class', 'visWrapper')
- .datum({ series: [] });
+ newEl = d3
+ .select('body')
+ .append('div')
+ .attr('class', 'visWrapper')
+ .datum({ series: [] });
- newEl.append('div').attr('class', 'visAxis__splitTitles--x');
- newEl.append('div').attr('class', 'visAxis__splitTitles--y');
- newEl.select('.visAxis__splitTitles--x').call(chartTitleSplit);
- newEl.select('.visAxis__splitTitles--y').call(chartTitleSplit);
+ newEl.append('div').attr('class', 'visAxis__splitTitles--x');
+ newEl.append('div').attr('class', 'visAxis__splitTitles--y');
+ newEl.select('.visAxis__splitTitles--x').call(chartTitleSplit);
+ newEl.select('.visAxis__splitTitles--y').call(chartTitleSplit);
- fixture = newEl.selectAll(this.childNodes)[0].length;
- })
- );
+ fixture = newEl.selectAll(this.childNodes)[0].length;
+ });
afterEach(function() {
newEl.remove();
@@ -237,17 +230,15 @@ describe('Vislib Split Function Test Suite', function() {
let fixture;
let divs;
- beforeEach(
- ngMock.inject(function() {
- fixture = d3
- .select('body')
- .append('div')
- .attr('class', 'columns')
- .datum({ columns: [{}, {}] });
- d3.select('.columns').call(xAxisSplit);
- divs = d3.selectAll('.x-axis-div')[0];
- })
- );
+ beforeEach(function() {
+ fixture = d3
+ .select('body')
+ .append('div')
+ .attr('class', 'columns')
+ .datum({ columns: [{}, {}] });
+ d3.select('.columns').call(xAxisSplit);
+ divs = d3.selectAll('.x-axis-div')[0];
+ });
afterEach(function() {
fixture.remove();
@@ -263,19 +254,17 @@ describe('Vislib Split Function Test Suite', function() {
let fixture;
let divs;
- beforeEach(
- ngMock.inject(function() {
- fixture = d3
- .select('body')
- .append('div')
- .attr('class', 'rows')
- .datum({ rows: [{}, {}] });
+ beforeEach(function() {
+ fixture = d3
+ .select('body')
+ .append('div')
+ .attr('class', 'rows')
+ .datum({ rows: [{}, {}] });
- d3.select('.rows').call(yAxisSplit);
+ d3.select('.rows').call(yAxisSplit);
- divs = d3.selectAll('.y-axis-div')[0];
- })
- );
+ divs = d3.selectAll('.y-axis-div')[0];
+ });
afterEach(function() {
fixture.remove();
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/gauge_chart/splits.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/gauge_chart/splits.js
index 0c744e4fa2b57..8978f80f58dde 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/gauge_chart/splits.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/gauge_chart/splits.js
@@ -18,9 +18,9 @@
*/
import d3 from 'd3';
-import ngMock from 'ng_mock';
import expect from '@kbn/expect';
import $ from 'jquery';
+
import { chartSplit } from '../../../../../lib/layout/splits/gauge_chart/chart_split';
import { chartTitleSplit } from '../../../../../lib/layout/splits/gauge_chart/chart_title_split';
@@ -148,16 +148,13 @@ describe('Vislib Gauge Split Function Test Suite', function() {
],
};
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function() {
- el = d3
- .select('body')
- .append('div')
- .attr('class', 'visualization')
- .datum(data);
- })
- );
+ beforeEach(function() {
+ el = d3
+ .select('body')
+ .append('div')
+ .attr('class', 'visualization')
+ .datum(data);
+ });
afterEach(function() {
el.remove();
@@ -166,11 +163,9 @@ describe('Vislib Gauge Split Function Test Suite', function() {
describe('chart split function', function() {
let fixture;
- beforeEach(
- ngMock.inject(function() {
- fixture = d3.select('.visualization').call(chartSplit);
- })
- );
+ beforeEach(function() {
+ fixture = d3.select('.visualization').call(chartSplit);
+ });
afterEach(function() {
fixture.remove();
@@ -188,15 +183,13 @@ describe('Vislib Gauge Split Function Test Suite', function() {
describe('chart title split function', function() {
let visEl;
- beforeEach(
- ngMock.inject(function() {
- visEl = el.append('div').attr('class', 'visWrapper');
- visEl.append('div').attr('class', 'visAxis__splitTitles--x');
- visEl.append('div').attr('class', 'visAxis__splitTitles--y');
- visEl.select('.visAxis__splitTitles--x').call(chartTitleSplit);
- visEl.select('.visAxis__splitTitles--y').call(chartTitleSplit);
- })
- );
+ beforeEach(function() {
+ visEl = el.append('div').attr('class', 'visWrapper');
+ visEl.append('div').attr('class', 'visAxis__splitTitles--x');
+ visEl.append('div').attr('class', 'visAxis__splitTitles--y');
+ visEl.select('.visAxis__splitTitles--x').call(chartTitleSplit);
+ visEl.select('.visAxis__splitTitles--y').call(chartTitleSplit);
+ });
afterEach(function() {
visEl.remove();
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/types/column_layout.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/types/column_layout.js
index bdf886033d8d6..e9c2ff0d2fa07 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/types/column_layout.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/types/column_layout.js
@@ -19,8 +19,8 @@
import d3 from 'd3';
import _ from 'lodash';
-import ngMock from 'ng_mock';
import expect from '@kbn/expect';
+
import { layoutTypes } from '../../../../lib/layout/layout_types';
describe('Vislib Column Layout Test Suite', function() {
@@ -85,16 +85,13 @@ describe('Vislib Column Layout Test Suite', function() {
yAxisLabel: 'Count',
};
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function() {
- el = d3
- .select('body')
- .append('div')
- .attr('class', 'visualization');
- columnLayout = layoutTypes.point_series(el, data);
- })
- );
+ beforeEach(function() {
+ el = d3
+ .select('body')
+ .append('div')
+ .attr('class', 'visualization');
+ columnLayout = layoutTypes.point_series(el, data);
+ });
afterEach(function() {
el.remove();
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/types/point_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/types/point_series.js
index ee3f5a476cd9a..03646d08298dd 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/types/point_series.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/types/point_series.js
@@ -17,12 +17,13 @@
* under the License.
*/
-import ngMock from 'ng_mock';
import expect from '@kbn/expect';
-import { vislibPointSeriesTypes as pointSeriesConfig } from '../../../lib/types/point_series';
+
import percentileTestdata from './testdata_linechart_percentile.json';
import percentileTestdataResult from './testdata_linechart_percentile_result.json';
+import { vislibPointSeriesTypes as pointSeriesConfig } from '../../../lib/types/point_series';
+
describe('Point Series Config Type Class Test Suite', function() {
let parsedConfig;
const histogramConfig = {
@@ -95,8 +96,6 @@ describe('Point Series Config Type Class Test Suite', function() {
},
};
- beforeEach(ngMock.module('kibana'));
-
describe('histogram chart', function() {
beforeEach(function() {
parsedConfig = pointSeriesConfig.column(histogramConfig, data);
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/vis_config.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/vis_config.js
index 3f0253b4a4670..7dfd2ded36a66 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/vis_config.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/vis_config.js
@@ -18,10 +18,10 @@
*/
import d3 from 'd3';
-import ngMock from 'ng_mock';
import expect from '@kbn/expect';
-import 'ui/persisted_state';
+
import { VisConfig } from '../../lib/vis_config';
+import { getMockUiState } from './fixtures/_vis_fixture';
describe('Vislib VisConfig Class Test Suite', function() {
let el;
@@ -85,27 +85,23 @@ describe('Vislib VisConfig Class Test Suite', function() {
yAxisLabel: 'Count',
};
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function($injector) {
- const PersistedState = $injector.get('PersistedState');
- el = d3
- .select('body')
- .append('div')
- .attr('class', 'visWrapper')
- .node();
+ beforeEach(() => {
+ el = d3
+ .select('body')
+ .append('div')
+ .attr('class', 'visWrapper')
+ .node();
- visConfig = new VisConfig(
- {
- type: 'point_series',
- },
- data,
- new PersistedState(),
- el,
- () => undefined
- );
- })
- );
+ visConfig = new VisConfig(
+ {
+ type: 'point_series',
+ },
+ data,
+ getMockUiState(),
+ el,
+ () => undefined
+ );
+ });
afterEach(() => {
el.remove();
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/x_axis.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/x_axis.js
index 09fe067537c7f..d42562a87b825 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/x_axis.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/x_axis.js
@@ -19,15 +19,15 @@
import d3 from 'd3';
import _ from 'lodash';
-import ngMock from 'ng_mock';
import expect from '@kbn/expect';
-import 'ui/persisted_state';
import $ from 'jquery';
+
import { Axis } from '../../lib/axis';
import { VisConfig } from '../../lib/vis_config';
+import { getMockUiState } from './fixtures/_vis_fixture';
describe('Vislib xAxis Class Test Suite', function() {
- let persistedState;
+ let mockUiState;
let xAxis;
let el;
let fixture;
@@ -105,34 +105,30 @@ describe('Vislib xAxis Class Test Suite', function() {
yAxisLabel: 'Count',
};
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function($injector) {
- persistedState = new ($injector.get('PersistedState'))();
-
- el = d3
- .select('body')
- .append('div')
- .attr('class', 'visAxis--x')
- .style('height', '40px');
+ beforeEach(() => {
+ mockUiState = getMockUiState();
+ el = d3
+ .select('body')
+ .append('div')
+ .attr('class', 'visAxis--x')
+ .style('height', '40px');
- fixture = el.append('div').attr('class', 'x-axis-div');
+ fixture = el.append('div').attr('class', 'x-axis-div');
- const visConfig = new VisConfig(
- {
- type: 'histogram',
- },
- data,
- persistedState,
- $('.x-axis-div')[0],
- () => undefined
- );
- xAxis = new Axis(visConfig, {
- type: 'category',
- id: 'CategoryAxis-1',
- });
- })
- );
+ const visConfig = new VisConfig(
+ {
+ type: 'histogram',
+ },
+ data,
+ mockUiState,
+ $('.x-axis-div')[0],
+ () => undefined
+ );
+ xAxis = new Axis(visConfig, {
+ type: 'category',
+ id: 'CategoryAxis-1',
+ });
+ });
afterEach(function() {
fixture.remove();
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/y_axis.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/y_axis.js
index e857aca3bf3ed..f73011d661645 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/y_axis.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/y_axis.js
@@ -19,15 +19,15 @@
import _ from 'lodash';
import d3 from 'd3';
-import ngMock from 'ng_mock';
-import expect from '@kbn/expect';
-import 'ui/persisted_state';
import $ from 'jquery';
+import expect from '@kbn/expect';
+
import { Axis } from '../../lib/axis';
import { VisConfig } from '../../lib/vis_config';
+import { getMockUiState } from './fixtures/_vis_fixture';
const YAxis = Axis;
-let persistedState;
+let mockUiState;
let el;
let buildYAxis;
let yAxis;
@@ -96,7 +96,7 @@ function createData(seriesData) {
type: 'histogram',
},
data,
- persistedState,
+ mockUiState,
node,
() => undefined
);
@@ -121,15 +121,10 @@ function createData(seriesData) {
}
describe('Vislib yAxis Class Test Suite', function() {
- beforeEach(ngMock.module('kibana'));
-
- beforeEach(
- ngMock.inject(function($injector) {
- persistedState = new ($injector.get('PersistedState'))();
-
- expect($('.y-axis-wrapper')).to.have.length(0);
- })
- );
+ beforeEach(() => {
+ mockUiState = getMockUiState();
+ expect($('.y-axis-wrapper')).to.have.length(0);
+ });
afterEach(function() {
if (el) {
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/vis.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/vis.js
index a6d1c4daf5d2c..4852f71d8c45b 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/vis.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/vis.js
@@ -19,19 +19,15 @@
import _ from 'lodash';
import $ from 'jquery';
-import ngMock from 'ng_mock';
-
import expect from '@kbn/expect';
-import 'ui/persisted_state';
import series from './lib/fixtures/mock_data/date_histogram/_series';
import columns from './lib/fixtures/mock_data/date_histogram/_columns';
import rows from './lib/fixtures/mock_data/date_histogram/_rows';
import stackedSeries from './lib/fixtures/mock_data/date_histogram/_stacked_series';
-import getFixturesVislibVisFixtureProvider from './lib/fixtures/_vis_fixture';
+import { getVis, getMockUiState } from './lib/fixtures/_vis_fixture';
const dataArray = [series, columns, rows, stackedSeries];
-
const names = ['series', 'columns', 'rows', 'stackedSeries'];
dataArray.forEach(function(data, i) {
@@ -39,19 +35,15 @@ dataArray.forEach(function(data, i) {
const beforeEvent = 'click';
const afterEvent = 'brush';
let vis;
- let persistedState;
+ let mockUiState;
let secondVis;
let numberOfCharts;
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function(Private, $injector) {
- const getVis = getFixturesVislibVisFixtureProvider(Private);
- vis = getVis();
- persistedState = new ($injector.get('PersistedState'))();
- secondVis = getVis();
- })
- );
+ beforeEach(() => {
+ vis = getVis();
+ secondVis = getVis();
+ mockUiState = getMockUiState();
+ });
afterEach(function() {
vis.destroy();
@@ -60,7 +52,7 @@ dataArray.forEach(function(data, i) {
describe('render Method', function() {
beforeEach(function() {
- vis.render(data, persistedState);
+ vis.render(data, mockUiState);
numberOfCharts = vis.handler.charts.length;
});
@@ -78,7 +70,7 @@ dataArray.forEach(function(data, i) {
it('should throw an error if no data is provided', function() {
expect(function() {
- vis.render(null, persistedState);
+ vis.render(null, mockUiState);
}).to.throwError();
});
});
@@ -91,8 +83,8 @@ dataArray.forEach(function(data, i) {
describe('destroy Method', function() {
beforeEach(function() {
- vis.render(data, persistedState);
- secondVis.render(data, persistedState);
+ vis.render(data, mockUiState);
+ secondVis.render(data, mockUiState);
secondVis.destroy();
});
@@ -107,7 +99,7 @@ dataArray.forEach(function(data, i) {
describe('set Method', function() {
beforeEach(function() {
- vis.render(data, persistedState);
+ vis.render(data, mockUiState);
vis.set('addLegend', false);
vis.set('offset', 'wiggle');
});
@@ -120,7 +112,7 @@ dataArray.forEach(function(data, i) {
describe('get Method', function() {
beforeEach(function() {
- vis.render(data, persistedState);
+ vis.render(data, mockUiState);
});
it('should get attribute values', function() {
@@ -142,7 +134,7 @@ dataArray.forEach(function(data, i) {
});
// Render chart
- vis.render(data, persistedState);
+ vis.render(data, mockUiState);
// Add event after charts have rendered
listeners.forEach(function(listener) {
@@ -196,7 +188,7 @@ dataArray.forEach(function(data, i) {
vis.off(beforeEvent, listener1);
// Render chart
- vis.render(data, persistedState);
+ vis.render(data, mockUiState);
// Add event after charts have rendered
listeners.forEach(function(listener) {
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/area_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/area_chart.js
index 7fe350bd85e05..c3f5859eb454c 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/area_chart.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/area_chart.js
@@ -18,14 +18,11 @@
*/
import d3 from 'd3';
-import ngMock from 'ng_mock';
import _ from 'lodash';
import $ from 'jquery';
-
import expect from '@kbn/expect';
-import 'ui/persisted_state';
-import getFixturesVislibVisFixtureProvider from '../lib/fixtures/_vis_fixture';
+import { getVis, getMockUiState } from '../lib/fixtures/_vis_fixture';
const dataTypesArray = {
'series pos': require('../lib/fixtures/mock_data/date_histogram/_series'),
@@ -46,18 +43,14 @@ const visLibParams = {
_.forOwn(dataTypesArray, function(dataType, dataTypeName) {
describe('Vislib Area Chart Test Suite for ' + dataTypeName + ' Data', function() {
let vis;
- let persistedState;
-
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function(Private, $injector) {
- const getVis = getFixturesVislibVisFixtureProvider(Private);
- vis = getVis(visLibParams);
- persistedState = new ($injector.get('PersistedState'))();
- vis.on('brush', _.noop);
- vis.render(dataType, persistedState);
- })
- );
+ let mockUiState;
+
+ beforeEach(() => {
+ vis = getVis(visLibParams);
+ mockUiState = getMockUiState();
+ vis.on('brush', _.noop);
+ vis.render(dataType, mockUiState);
+ });
afterEach(function() {
vis.destroy();
@@ -97,17 +90,15 @@ _.forOwn(dataTypesArray, function(dataType, dataTypeName) {
let d3selectedPath;
let onMouseOver;
- beforeEach(
- ngMock.inject(function() {
- vis.handler.charts.forEach(function(chart) {
- path = $(chart.chartEl).find('path')[0];
- d3selectedPath = d3.select(path)[0][0];
+ beforeEach(function() {
+ vis.handler.charts.forEach(function(chart) {
+ path = $(chart.chartEl).find('path')[0];
+ d3selectedPath = d3.select(path)[0][0];
- // d3 instance of click and hover
- onMouseOver = !!d3selectedPath.__onmouseover;
- });
- })
- );
+ // d3 instance of click and hover
+ onMouseOver = !!d3selectedPath.__onmouseover;
+ });
+ });
it('should attach a hover event', function() {
vis.handler.charts.forEach(function() {
@@ -124,20 +115,18 @@ _.forOwn(dataTypesArray, function(dataType, dataTypeName) {
let onClick;
let onMouseOver;
- beforeEach(
- ngMock.inject(function() {
- vis.handler.charts.forEach(function(chart) {
- circle = $(chart.chartEl).find('circle')[0];
- brush = $(chart.chartEl).find('.brush');
- d3selectedCircle = d3.select(circle)[0][0];
-
- // d3 instance of click and hover
- onBrush = !!brush;
- onClick = !!d3selectedCircle.__onclick;
- onMouseOver = !!d3selectedCircle.__onmouseover;
- });
- })
- );
+ beforeEach(() => {
+ vis.handler.charts.forEach(function(chart) {
+ circle = $(chart.chartEl).find('circle')[0];
+ brush = $(chart.chartEl).find('.brush');
+ d3selectedCircle = d3.select(circle)[0][0];
+
+ // d3 instance of click and hover
+ onBrush = !!brush;
+ onClick = !!d3selectedCircle.__onclick;
+ onMouseOver = !!d3selectedCircle.__onmouseover;
+ });
+ });
// D3 brushing requires that a g element is appended that
// listens for mousedown events. This g element includes
@@ -219,7 +208,7 @@ _.forOwn(dataTypesArray, function(dataType, dataTypeName) {
describe('defaultYExtents is true', function() {
beforeEach(function() {
vis.visConfigArgs.defaultYExtents = true;
- vis.render(dataType, persistedState);
+ vis.render(dataType, mockUiState);
});
it('should return yAxis extents equal to data extents', function() {
@@ -238,7 +227,7 @@ _.forOwn(dataTypesArray, function(dataType, dataTypeName) {
beforeEach(function() {
vis.visConfigArgs.defaultYExtents = true;
vis.visConfigArgs.boundsMargin = boundsMarginValue;
- vis.render(dataType, persistedState);
+ vis.render(dataType, mockUiState);
});
it('should return yAxis extents equal to data extents with boundsMargin', function() {
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/chart.js
index 088d3377af4dd..9653f9abab6fb 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/chart.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/chart.js
@@ -18,16 +18,12 @@
*/
import d3 from 'd3';
-import ngMock from 'ng_mock';
-
import expect from '@kbn/expect';
-import 'ui/persisted_state';
import { Chart } from '../../visualizations/_chart';
-import getFixturesVislibVisFixtureProvider from '../lib/fixtures/_vis_fixture';
+import { getVis, getMockUiState } from '../lib/fixtures/_vis_fixture';
describe('Vislib _chart Test Suite', function() {
- let persistedState;
let vis;
let el;
let myChart;
@@ -109,30 +105,24 @@ describe('Vislib _chart Test Suite', function() {
yAxisLabel: 'Count',
};
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function(Private, $injector) {
- const getVis = getFixturesVislibVisFixtureProvider(Private);
- persistedState = new ($injector.get('PersistedState'))();
-
- el = d3
- .select('body')
- .append('div')
- .attr('class', 'column-chart');
+ beforeEach(() => {
+ el = d3
+ .select('body')
+ .append('div')
+ .attr('class', 'column-chart');
- config = {
- type: 'histogram',
- addTooltip: true,
- addLegend: true,
- zeroFill: true,
- };
+ config = {
+ type: 'histogram',
+ addTooltip: true,
+ addLegend: true,
+ zeroFill: true,
+ };
- vis = getVis(config, el[0][0]);
- vis.render(data, persistedState);
+ vis = getVis(config, el[0][0]);
+ vis.render(data, getMockUiState());
- myChart = vis.handler.charts[0];
- })
- );
+ myChart = vis.handler.charts[0];
+ });
afterEach(function() {
el.remove();
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/column_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/column_chart.js
index d02060ef29bdd..2216294fcbac1 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/column_chart.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/column_chart.js
@@ -17,12 +17,10 @@
* under the License.
*/
-import ngMock from 'ng_mock';
import _ from 'lodash';
import d3 from 'd3';
-
+import $ from 'jquery';
import expect from '@kbn/expect';
-import 'ui/persisted_state';
// Data
import series from '../lib/fixtures/mock_data/date_histogram/_series';
@@ -31,11 +29,11 @@ import seriesNeg from '../lib/fixtures/mock_data/date_histogram/_series_neg';
import termsColumns from '../lib/fixtures/mock_data/terms/_columns';
import histogramRows from '../lib/fixtures/mock_data/histogram/_rows';
import stackedSeries from '../lib/fixtures/mock_data/date_histogram/_stacked_series';
+
import { seriesMonthlyInterval } from '../lib/fixtures/mock_data/date_histogram/_series_monthly_interval';
import { rowsSeriesWithHoles } from '../lib/fixtures/mock_data/date_histogram/_rows_series_with_holes';
import rowsWithZeros from '../lib/fixtures/mock_data/date_histogram/_rows';
-import $ from 'jquery';
-import getFixturesVislibVisFixtureProvider from '../lib/fixtures/_vis_fixture';
+import { getVis, getMockUiState } from '../lib/fixtures/_vis_fixture';
// tuple, with the format [description, mode, data]
const dataTypesArray = [
@@ -54,7 +52,7 @@ dataTypesArray.forEach(function(dataType) {
describe('Vislib Column Chart Test Suite for ' + name + ' Data', function() {
let vis;
- let persistedState;
+ let mockUiState;
const visLibParams = {
type: 'histogram',
addLegend: true,
@@ -67,16 +65,12 @@ dataTypesArray.forEach(function(dataType) {
},
};
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function(Private, $injector) {
- const getVis = getFixturesVislibVisFixtureProvider(Private);
- vis = getVis(visLibParams);
- persistedState = new ($injector.get('PersistedState'))();
- vis.on('brush', _.noop);
- vis.render(data, persistedState);
- })
- );
+ beforeEach(() => {
+ vis = getVis(visLibParams);
+ mockUiState = getMockUiState();
+ vis.on('brush', _.noop);
+ vis.render(data, mockUiState);
+ });
afterEach(function() {
vis.destroy();
@@ -200,7 +194,7 @@ dataTypesArray.forEach(function(dataType) {
describe('defaultYExtents is true', function() {
beforeEach(function() {
vis.visConfigArgs.defaultYExtents = true;
- vis.render(data, persistedState);
+ vis.render(data, mockUiState);
});
it('should return yAxis extents equal to data extents', function() {
@@ -219,7 +213,7 @@ dataTypesArray.forEach(function(dataType) {
beforeEach(function() {
vis.visConfigArgs.defaultYExtents = true;
vis.visConfigArgs.boundsMargin = boundsMarginValue;
- vis.render(data, persistedState);
+ vis.render(data, mockUiState);
});
it('should return yAxis extents equal to data extents with boundsMargin', function() {
@@ -247,7 +241,7 @@ dataTypesArray.forEach(function(dataType) {
describe('stackData method - data set with zeros in percentage mode', function() {
let vis;
- let persistedState;
+ let mockUiState;
const visLibParams = {
type: 'histogram',
addLegend: true,
@@ -256,22 +250,18 @@ describe('stackData method - data set with zeros in percentage mode', function()
zeroFill: true,
};
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function(Private, $injector) {
- const getVis = getFixturesVislibVisFixtureProvider(Private);
- vis = getVis(visLibParams);
- persistedState = new ($injector.get('PersistedState'))();
- vis.on('brush', _.noop);
- })
- );
+ beforeEach(() => {
+ vis = getVis(visLibParams);
+ mockUiState = getMockUiState();
+ vis.on('brush', _.noop);
+ });
afterEach(function() {
vis.destroy();
});
it('should not mutate the injected zeros', function() {
- vis.render(seriesMonthlyInterval, persistedState);
+ vis.render(seriesMonthlyInterval, mockUiState);
expect(vis.handler.charts).to.have.length(1);
const chart = vis.handler.charts[0];
@@ -284,7 +274,7 @@ describe('stackData method - data set with zeros in percentage mode', function()
});
it('should not mutate zeros that exist in the data', function() {
- vis.render(rowsWithZeros, persistedState);
+ vis.render(rowsWithZeros, mockUiState);
expect(vis.handler.charts).to.have.length(2);
const chart = vis.handler.charts[0];
@@ -298,7 +288,7 @@ describe('stackData method - data set with zeros in percentage mode', function()
describe('datumWidth - split chart data set with holes', function() {
let vis;
- let persistedState;
+ let mockUiState;
const visLibParams = {
type: 'histogram',
addLegend: true,
@@ -307,16 +297,12 @@ describe('datumWidth - split chart data set with holes', function() {
zeroFill: true,
};
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function(Private, $injector) {
- const getVis = getFixturesVislibVisFixtureProvider(Private);
- vis = getVis(visLibParams);
- persistedState = new ($injector.get('PersistedState'))();
- vis.on('brush', _.noop);
- vis.render(rowsSeriesWithHoles, persistedState);
- })
- );
+ beforeEach(() => {
+ vis = getVis(visLibParams);
+ mockUiState = getMockUiState();
+ vis.on('brush', _.noop);
+ vis.render(rowsSeriesWithHoles, mockUiState);
+ });
afterEach(function() {
vis.destroy();
@@ -336,7 +322,7 @@ describe('datumWidth - split chart data set with holes', function() {
describe('datumWidth - monthly interval', function() {
let vis;
- let persistedState;
+ let mockUiState;
const visLibParams = {
type: 'histogram',
addLegend: true,
@@ -345,16 +331,12 @@ describe('datumWidth - monthly interval', function() {
zeroFill: true,
};
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function(Private, $injector) {
- const getVis = getFixturesVislibVisFixtureProvider(Private);
- vis = getVis(visLibParams);
- persistedState = new ($injector.get('PersistedState'))();
- vis.on('brush', _.noop);
- vis.render(seriesMonthlyInterval, persistedState);
- })
- );
+ beforeEach(() => {
+ vis = getVis(visLibParams);
+ mockUiState = getMockUiState();
+ vis.on('brush', _.noop);
+ vis.render(seriesMonthlyInterval, mockUiState);
+ });
afterEach(function() {
vis.destroy();
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/gauge_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/gauge_chart.js
index 074b34e1c03c4..fe25734fcbfde 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/gauge_chart.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/gauge_chart.js
@@ -17,21 +17,15 @@
* under the License.
*/
-import ngMock from 'ng_mock';
import $ from 'jquery';
import _ from 'lodash';
-
import expect from '@kbn/expect';
-import 'ui/persisted_state';
import data from '../lib/fixtures/mock_data/terms/_seriesMultiple';
-import getFixturesVislibVisFixtureProvider from '../lib/fixtures/_vis_fixture';
+import { getVis, getMockUiState } from '../lib/fixtures/_vis_fixture';
describe('Vislib Gauge Chart Test Suite', function() {
- let PersistedState;
- let vislibVis;
let vis;
- let persistedState;
let chartEl;
const visLibParams = {
type: 'gauge',
@@ -81,21 +75,15 @@ describe('Vislib Gauge Chart Test Suite', function() {
vis.destroy();
$('.visChart').remove();
}
- vis = vislibVis(config);
- persistedState = new PersistedState();
+ vis = getVis(config);
vis.on('brush', _.noop);
- vis.render(data, persistedState);
+ vis.render(data, getMockUiState());
chartEl = vis.handler.charts[0].chartEl;
}
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function(Private, $injector) {
- vislibVis = getFixturesVislibVisFixtureProvider(Private);
- PersistedState = $injector.get('PersistedState');
- generateVis();
- })
- );
+ beforeEach(() => {
+ generateVis();
+ });
afterEach(function() {
vis.destroy();
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/heatmap_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/heatmap_chart.js
index bf1dbad0b44cf..f4c952be191de 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/heatmap_chart.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/heatmap_chart.js
@@ -17,12 +17,10 @@
* under the License.
*/
-import ngMock from 'ng_mock';
import _ from 'lodash';
+import $ from 'jquery';
import d3 from 'd3';
-
import expect from '@kbn/expect';
-import 'ui/persisted_state';
// Data
import series from '../lib/fixtures/mock_data/date_histogram/_series';
@@ -30,8 +28,8 @@ import seriesPosNeg from '../lib/fixtures/mock_data/date_histogram/_series_pos_n
import seriesNeg from '../lib/fixtures/mock_data/date_histogram/_series_neg';
import termsColumns from '../lib/fixtures/mock_data/terms/_columns';
import stackedSeries from '../lib/fixtures/mock_data/date_histogram/_stacked_series';
-import $ from 'jquery';
-import getFixturesVislibVisFixtureProvider from '../lib/fixtures/_vis_fixture';
+
+import { getVis, getMockUiState } from '../lib/fixtures/_vis_fixture';
// tuple, with the format [description, mode, data]
const dataTypesArray = [
@@ -48,10 +46,8 @@ describe('Vislib Heatmap Chart Test Suite', function() {
const data = dataType[1];
describe('for ' + name + ' Data', function() {
- let PersistedState;
- let vislibVis;
let vis;
- let persistedState;
+ let mockUiState;
const visLibParams = {
type: 'heatmap',
addLegend: true,
@@ -66,20 +62,15 @@ describe('Vislib Heatmap Chart Test Suite', function() {
function generateVis(opts = {}) {
const config = _.defaultsDeep({}, opts, visLibParams);
- vis = vislibVis(config);
- persistedState = new PersistedState();
+ vis = getVis(config);
+ mockUiState = getMockUiState();
vis.on('brush', _.noop);
- vis.render(data, persistedState);
+ vis.render(data, mockUiState);
}
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function(Private, $injector) {
- vislibVis = getFixturesVislibVisFixtureProvider(Private);
- PersistedState = $injector.get('PersistedState');
- generateVis();
- })
- );
+ beforeEach(() => {
+ generateVis();
+ });
afterEach(function() {
vis.destroy();
@@ -174,7 +165,7 @@ describe('Vislib Heatmap Chart Test Suite', function() {
});
it('should define default colors', function() {
- expect(persistedState.get('vis.defaultColors')).to.not.be(undefined);
+ expect(mockUiState.get('vis.defaultColors')).to.not.be(undefined);
});
it('should set custom range', function() {
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/line_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/line_chart.js
index d010944a19e47..1269fe7bcf62e 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/line_chart.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/line_chart.js
@@ -19,12 +19,9 @@
import d3 from 'd3';
import expect from '@kbn/expect';
-import ngMock from 'ng_mock';
import $ from 'jquery';
import _ from 'lodash';
-import 'ui/persisted_state';
-
// Data
import seriesPos from '../lib/fixtures/mock_data/date_histogram/_series';
import seriesPosNeg from '../lib/fixtures/mock_data/date_histogram/_series_pos_neg';
@@ -32,7 +29,8 @@ import seriesNeg from '../lib/fixtures/mock_data/date_histogram/_series_neg';
import histogramColumns from '../lib/fixtures/mock_data/histogram/_columns';
import rangeRows from '../lib/fixtures/mock_data/range/_rows';
import termSeries from '../lib/fixtures/mock_data/terms/_series';
-import getFixturesVislibVisFixtureProvider from '../lib/fixtures/_vis_fixture';
+
+import { getVis, getMockUiState } from '../lib/fixtures/_vis_fixture';
const dataTypes = [
['series pos', seriesPos],
@@ -50,25 +48,21 @@ describe('Vislib Line Chart', function() {
describe(name + ' Data', function() {
let vis;
- let persistedState;
-
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function(Private, $injector) {
- const getVis = getFixturesVislibVisFixtureProvider(Private);
- const visLibParams = {
- type: 'line',
- addLegend: true,
- addTooltip: true,
- drawLinesBetweenPoints: true,
- };
-
- vis = getVis(visLibParams);
- persistedState = new ($injector.get('PersistedState'))();
- vis.render(data, persistedState);
- vis.on('brush', _.noop);
- })
- );
+ let mockUiState;
+
+ beforeEach(() => {
+ const visLibParams = {
+ type: 'line',
+ addLegend: true,
+ addTooltip: true,
+ drawLinesBetweenPoints: true,
+ };
+
+ vis = getVis(visLibParams);
+ mockUiState = getMockUiState();
+ vis.render(data, mockUiState);
+ vis.on('brush', _.noop);
+ });
afterEach(function() {
vis.destroy();
@@ -82,20 +76,18 @@ describe('Vislib Line Chart', function() {
let onClick;
let onMouseOver;
- beforeEach(
- ngMock.inject(function() {
- vis.handler.charts.forEach(function(chart) {
- circle = $(chart.chartEl).find('.circle')[0];
- brush = $(chart.chartEl).find('.brush');
- d3selectedCircle = d3.select(circle)[0][0];
-
- // d3 instance of click and hover
- onBrush = !!brush;
- onClick = !!d3selectedCircle.__onclick;
- onMouseOver = !!d3selectedCircle.__onmouseover;
- });
- })
- );
+ beforeEach(function() {
+ vis.handler.charts.forEach(function(chart) {
+ circle = $(chart.chartEl).find('.circle')[0];
+ brush = $(chart.chartEl).find('.brush');
+ d3selectedCircle = d3.select(circle)[0][0];
+
+ // d3 instance of click and hover
+ onBrush = !!brush;
+ onClick = !!d3selectedCircle.__onclick;
+ onMouseOver = !!d3selectedCircle.__onmouseover;
+ });
+ });
// D3 brushing requires that a g element is appended that
// listens for mousedown events. This g element includes
@@ -177,7 +169,7 @@ describe('Vislib Line Chart', function() {
describe('defaultYExtents is true', function() {
beforeEach(function() {
vis.visConfigArgs.defaultYExtents = true;
- vis.render(data, persistedState);
+ vis.render(data, mockUiState);
});
it('should return yAxis extents equal to data extents', function() {
@@ -196,7 +188,7 @@ describe('Vislib Line Chart', function() {
beforeEach(function() {
vis.visConfigArgs.defaultYExtents = true;
vis.visConfigArgs.boundsMargin = boundsMarginValue;
- vis.render(data, persistedState);
+ vis.render(data, mockUiState);
});
it('should return yAxis extents equal to data extents with boundsMargin', function() {
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js
index 381dfcd387cc2..f38fa47393a14 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js
@@ -18,19 +18,16 @@
*/
import d3 from 'd3';
-import ngMock from 'ng_mock';
import _ from 'lodash';
import $ from 'jquery';
-
import expect from '@kbn/expect';
-// TODO: Remove ui imports once converting to jest
-import 'ui/persisted_state';
+
import { vislibSlicesResponseHandlerProvider } from 'ui/vis/response_handlers/vislib';
import fixtures from 'fixtures/fake_hierarchical_data';
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
-import getFixturesVislibVisFixtureProvider from '../lib/fixtures/_vis_fixture';
+import { getVis, getMockUiState } from '../lib/fixtures/_vis_fixture';
import { Vis, tabifyAggResponse } from '../../../legacy_imports';
const rowAgg = [
@@ -123,35 +120,31 @@ describe('No global chart settings', function() {
addTooltip: true,
};
let chart1;
- let persistedState;
+ let mockUiState;
let indexPattern;
let responseHandler;
let data1;
let stubVis1;
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function(Private, $injector) {
- const getVis = getFixturesVislibVisFixtureProvider(Private);
- chart1 = getVis(visLibParams1);
- persistedState = new ($injector.get('PersistedState'))();
- indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
- responseHandler = vislibSlicesResponseHandlerProvider().handler;
-
- let id1 = 1;
- stubVis1 = new Vis(indexPattern, {
- type: 'pie',
- aggs: rowAgg,
- });
+ beforeEach(() => {
+ chart1 = getVis(visLibParams1);
+ mockUiState = getMockUiState();
+ indexPattern = new FixturesStubbedLogstashIndexPatternProvider();
+ responseHandler = vislibSlicesResponseHandlerProvider().handler;
- stubVis1.isHierarchical = () => true;
+ let id1 = 1;
+ stubVis1 = new Vis(indexPattern, {
+ type: 'pie',
+ aggs: rowAgg,
+ });
- // We need to set the aggs to a known value.
- _.each(stubVis1.aggs.aggs, function(agg) {
- agg.id = 'agg_' + id1++;
- });
- })
- );
+ stubVis1.isHierarchical = () => true;
+
+ // We need to set the aggs to a known value.
+ _.each(stubVis1.aggs.aggs, function(agg) {
+ agg.id = 'agg_' + id1++;
+ });
+ });
beforeEach(async () => {
const table1 = tabifyAggResponse(stubVis1.aggs, fixtures.threeTermBuckets, {
@@ -159,7 +152,7 @@ describe('No global chart settings', function() {
});
data1 = await responseHandler(table1, rowAggDimensions);
- chart1.render(data1, persistedState);
+ chart1.render(data1, mockUiState);
});
afterEach(function() {
@@ -216,40 +209,37 @@ describe('Vislib PieChart Class Test Suite', function() {
addTooltip: true,
};
let vis;
- let persistedState;
+ let mockUiState;
let indexPattern;
let data;
let stubVis;
let responseHandler;
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function(Private, $injector) {
- const getVis = getFixturesVislibVisFixtureProvider(Private);
- vis = getVis(visLibParams);
- persistedState = new ($injector.get('PersistedState'))();
- indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
- responseHandler = vislibSlicesResponseHandlerProvider().handler;
-
- let id = 1;
- stubVis = new Vis(indexPattern, {
- type: 'pie',
- aggs: dataAgg,
- });
+ beforeEach(() => {
+ vis = getVis(visLibParams);
+ mockUiState = getMockUiState();
+ indexPattern = new FixturesStubbedLogstashIndexPatternProvider();
+ responseHandler = vislibSlicesResponseHandlerProvider().handler;
- // We need to set the aggs to a known value.
- _.each(stubVis.aggs.aggs, function(agg) {
- agg.id = 'agg_' + id++;
- });
- })
- );
+ let id = 1;
+ stubVis = new Vis(indexPattern, {
+ type: 'pie',
+ aggs: dataAgg,
+ });
+
+ // We need to set the aggs to a known value.
+ _.each(stubVis.aggs.aggs, function(agg) {
+ agg.id = 'agg_' + id++;
+ });
+ });
beforeEach(async () => {
const table = tabifyAggResponse(stubVis.aggs, fixtures.threeTermBuckets, {
metricsAtAllLevels: true,
});
data = await responseHandler(table, dataDimensions);
- vis.render(data, persistedState);
+
+ vis.render(data, mockUiState);
});
afterEach(function() {
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/time_marker.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/time_marker.js
index ec22d43c08cb2..d69f952325ed0 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/time_marker.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/time_marker.js
@@ -18,11 +18,11 @@
*/
import d3 from 'd3';
+import $ from 'jquery';
import expect from '@kbn/expect';
-import ngMock from 'ng_mock';
+
import series from '../lib/fixtures/mock_data/date_histogram/_series';
import terms from '../lib/fixtures/mock_data/terms/_columns';
-import $ from 'jquery';
import { TimeMarker } from '../../visualizations/time_marker';
describe('Vislib Time Marker Test Suite', function() {
@@ -57,26 +57,23 @@ describe('Vislib Time Marker Test Suite', function() {
let maxDomain;
let domain;
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function() {
- minDomain = getExtent(series.series, d3.min);
- maxDomain = getExtent(series.series, d3.max);
- domain = [minDomain, maxDomain];
- xScale = d3.time
- .scale()
- .domain(domain)
- .range([0, 500]);
- defaultMarker = new TimeMarker(times, xScale, height);
- customMarker = new TimeMarker(myTimes, xScale, height);
-
- selection = d3
- .select('body')
- .append('div')
- .attr('class', 'marker');
- selection.datum(series);
- })
- );
+ beforeEach(function() {
+ minDomain = getExtent(series.series, d3.min);
+ maxDomain = getExtent(series.series, d3.max);
+ domain = [minDomain, maxDomain];
+ xScale = d3.time
+ .scale()
+ .domain(domain)
+ .range([0, 500]);
+ defaultMarker = new TimeMarker(times, xScale, height);
+ customMarker = new TimeMarker(myTimes, xScale, height);
+
+ selection = d3
+ .select('body')
+ .append('div')
+ .attr('class', 'marker');
+ selection.datum(series);
+ });
afterEach(function() {
selection.remove('*');
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/vis_types.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/vis_types.js
index e150ca58c15cf..c8f0faf8dcca5 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/vis_types.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/vis_types.js
@@ -17,20 +17,17 @@
* under the License.
*/
-import expect from '@kbn/expect';
-import ngMock from 'ng_mock';
import _ from 'lodash';
+import expect from '@kbn/expect';
+
import { visTypes } from '../../visualizations/vis_types';
describe('Vislib Vis Types Test Suite', function() {
let visFunc;
- beforeEach(ngMock.module('kibana'));
- beforeEach(
- ngMock.inject(function() {
- visFunc = visTypes.point_series;
- })
- );
+ beforeEach(function() {
+ visFunc = visTypes.point_series;
+ });
it('should be an object', function() {
expect(_.isObject(visTypes)).to.be(true);
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/_index.scss b/src/legacy/core_plugins/vis_type_vislib/public/vislib/_index.scss
index 0344fbb5359ec..78e16224a67a3 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/_index.scss
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/_index.scss
@@ -1,6 +1,9 @@
@import './variables';
+@import './vislib_vis_type';
@import './lib/index';
+@import './components/tooltip/index';
+@import './components/legend/index';
@import './visualizations/point_series/index';
@import './visualizations/gauges/index';
diff --git a/src/legacy/ui/public/vis/vis_types/_vislib_vis_type.scss b/src/legacy/core_plugins/vis_type_vislib/public/vislib/_vislib_vis_type.scss
similarity index 99%
rename from src/legacy/ui/public/vis/vis_types/_vislib_vis_type.scss
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/_vislib_vis_type.scss
index c1f369d3e3536..c03aa19140de0 100644
--- a/src/legacy/ui/public/vis/vis_types/_vislib_vis_type.scss
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/_vislib_vis_type.scss
@@ -7,12 +7,15 @@
&.visLib--legend-left {
flex-direction: row-reverse;
}
+
&.visLib--legend-right {
flex-direction: row;
}
+
&.visLib--legend-top {
flex-direction: column-reverse;
}
+
&.visLib--legend-bottom {
flex-direction: column;
}
diff --git a/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/__snapshots__/vislib_vis_legend.test.tsx.snap b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap
similarity index 100%
rename from src/legacy/ui/public/vis/vis_types/vislib_vis_legend/__snapshots__/vislib_vis_legend.test.tsx.snap
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/_index.scss b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/_index.scss
new file mode 100644
index 0000000000000..53617a984dcf5
--- /dev/null
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/_index.scss
@@ -0,0 +1 @@
+@import './_legend';
diff --git a/src/legacy/ui/public/vis/vis_types/_vislib_vis_legend.scss b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/_legend.scss
similarity index 85%
rename from src/legacy/ui/public/vis/vis_types/_vislib_vis_legend.scss
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/_legend.scss
index 62050ce4e99fd..b1a59f88a348a 100644
--- a/src/legacy/ui/public/vis/vis_types/_vislib_vis_legend.scss
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/_legend.scss
@@ -1,4 +1,4 @@
-@import '../../../../core_plugins/vis_type_vislib/public/vislib/variables';
+@import '../../variables';
// NOTE: Some of the styles attempt to align with the TSVB legend
@@ -14,17 +14,16 @@ $visLegendLineHeight: $euiSize;
display: flex;
padding: $euiSizeXS;
background-color: $euiColorEmptyShade;
- transition: opacity $euiAnimSpeedFast $euiAnimSlightResistance,
- background-color $euiAnimSpeedFast $euiAnimSlightResistance $euiAnimSpeedExtraSlow;
+ transition: opacity $euiAnimSpeedFast $euiAnimSlightResistance, background-color $euiAnimSpeedFast $euiAnimSlightResistance $euiAnimSpeedExtraSlow;
&:focus {
box-shadow: none;
- background-color: $euiFocusBackgroundColor !important;
+ background-color: $euiFocusBackgroundColor !important; // sass-lint:disable-line no-important
}
}
.visLegend__toggle--isOpen {
- background-color: transparentize($euiColorDarkestShade, 0.9);
+ background-color: transparentize($euiColorDarkestShade, .9);
opacity: 1;
}
diff --git a/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/index.ts b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/index.ts
similarity index 94%
rename from src/legacy/ui/public/vis/vis_types/vislib_vis_legend/index.ts
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/index.ts
index ebf132f0ab697..230cda4c35ff8 100644
--- a/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/index.ts
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/index.ts
@@ -17,5 +17,5 @@
* under the License.
*/
-export { VisLegend } from './vislib_vis_legend';
+export { VisLegend } from './legend';
export { CUSTOM_LEGEND_VIS_TYPES } from './models';
diff --git a/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/vislib_vis_legend.test.tsx b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.test.tsx
similarity index 96%
rename from src/legacy/ui/public/vis/vis_types/vislib_vis_legend/vislib_vis_legend.test.tsx
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.test.tsx
index 839dc0024bbea..6f0a5a3784b07 100644
--- a/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/vislib_vis_legend.test.tsx
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.test.tsx
@@ -23,7 +23,7 @@ import { mount, ReactWrapper } from 'enzyme';
import { I18nProvider } from '@kbn/i18n/react';
import { EuiButtonGroup } from '@elastic/eui';
-import { VisLegend, VisLegendProps } from '../vislib_vis_legend/vislib_vis_legend';
+import { VisLegend, VisLegendProps } from './legend';
import { legendColors } from './models';
jest.mock('@elastic/eui', () => ({
@@ -31,10 +31,10 @@ jest.mock('@elastic/eui', () => ({
htmlIdGenerator: jest.fn().mockReturnValue(() => 'legendId'),
}));
-jest.mock('../../../visualize/loader/pipeline_helpers/utilities', () => ({
+jest.mock('../../../legacy_imports', () => ({
getTableAggs: jest.fn(),
}));
-jest.mock('../../../../../core_plugins/visualizations/public', () => ({
+jest.mock('../../../../../visualizations/public', () => ({
createFiltersFromEvent: jest.fn().mockReturnValue(['yes']),
}));
diff --git a/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/vislib_vis_legend.tsx b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx
similarity index 93%
rename from src/legacy/ui/public/vis/vis_types/vislib_vis_legend/vislib_vis_legend.tsx
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx
index d98590f9885b9..0eec557dd334e 100644
--- a/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/vislib_vis_legend.tsx
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx
@@ -24,11 +24,11 @@ import { i18n } from '@kbn/i18n';
import { EuiPopoverProps, EuiIcon, keyCodes, htmlIdGenerator } from '@elastic/eui';
// @ts-ignore
-import { createFiltersFromEvent } from '../../../../../core_plugins/visualizations/public';
+import { createFiltersFromEvent } from '../../../../../visualizations/public';
import { CUSTOM_LEGEND_VIS_TYPES, LegendItem } from './models';
-import { VisLegendItem } from './vislib_vis_legend_item';
+import { VisLegendItem } from './legend_item';
import { getPieNames } from './pie_utils';
-import { getTableAggs } from '../../../visualize/loader/pipeline_helpers/utilities';
+import { getTableAggs } from '../../../legacy_imports';
export interface VisLegendProps {
vis: any;
@@ -138,7 +138,7 @@ export class VisLegend extends PureComponent {
this.setState({
labels: [
{
- label: i18n.translate('common.ui.vis.visTypes.legend.loadingLabel', {
+ label: i18n.translate('visTypeVislib.vislib.legend.loadingLabel', {
defaultMessage: 'loading…',
}),
},
@@ -244,13 +244,13 @@ export class VisLegend extends PureComponent {
className={classNames('visLegend__toggle kbn-resetFocusState', {
'visLegend__toggle--isOpen': open,
})}
- aria-label={i18n.translate('common.ui.vis.visTypes.legend.toggleLegendButtonAriaLabel', {
+ aria-label={i18n.translate('visTypeVislib.vislib.legend.toggleLegendButtonAriaLabel', {
defaultMessage: 'Toggle legend',
})}
aria-expanded={Boolean(open)}
aria-controls={this.legendId}
data-test-subj="vislibToggleLegend"
- title={i18n.translate('common.ui.vis.visTypes.legend.toggleLegendButtonTitle', {
+ title={i18n.translate('visTypeVislib.vislib.legend.toggleLegendButtonTitle', {
defaultMessage: 'Toggle legend',
})}
>
diff --git a/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/vislib_vis_legend_item.tsx b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend_item.tsx
similarity index 92%
rename from src/legacy/ui/public/vis/vis_types/vislib_vis_legend/vislib_vis_legend_item.tsx
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend_item.tsx
index 7376fabfe738b..09c8a838532bf 100644
--- a/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/vislib_vis_legend_item.tsx
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend_item.tsx
@@ -77,7 +77,7 @@ const VisLegendItemComponent = ({
const filterOptions: EuiButtonGroupOption[] = [
{
id: 'filterIn',
- label: i18n.translate('common.ui.vis.visTypes.legend.filterForValueButtonAriaLabel', {
+ label: i18n.translate('visTypeVislib.vislib.legend.filterForValueButtonAriaLabel', {
defaultMessage: 'Filter for value {legendDataLabel}',
values: { legendDataLabel: item.label },
}),
@@ -86,7 +86,7 @@ const VisLegendItemComponent = ({
},
{
id: 'filterOut',
- label: i18n.translate('common.ui.vis.visTypes.legend.filterOutValueButtonAriaLabel', {
+ label: i18n.translate('visTypeVislib.vislib.legend.filterOutValueButtonAriaLabel', {
defaultMessage: 'Filter out value {legendDataLabel}',
values: { legendDataLabel: item.label },
}),
@@ -105,7 +105,7 @@ const VisLegendItemComponent = ({
type="multi"
isIconOnly
isFullWidth
- legend={i18n.translate('common.ui.vis.visTypes.legend.filterOptionsLegend', {
+ legend={i18n.translate('visTypeVislib.vislib.legend.filterOptionsLegend', {
defaultMessage: '{legendDataLabel}, filter options',
values: { legendDataLabel: item.label },
})}
@@ -131,7 +131,7 @@ const VisLegendItemComponent = ({
onBlur={onUnhighlight}
data-label={item.label}
title={item.label}
- aria-label={i18n.translate('common.ui.vis.visTypes.legend.toggleOptionsButtonAriaLabel', {
+ aria-label={i18n.translate('visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel', {
defaultMessage: '{legendDataLabel}, toggle options',
values: { legendDataLabel: item.label },
})}
@@ -163,7 +163,7 @@ const VisLegendItemComponent = ({
diff --git a/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/models.ts b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/models.ts
similarity index 100%
rename from src/legacy/ui/public/vis/vis_types/vislib_vis_legend/models.ts
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/models.ts
diff --git a/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/pie_utils.ts b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/pie_utils.ts
similarity index 100%
rename from src/legacy/ui/public/vis/vis_types/vislib_vis_legend/pie_utils.ts
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/pie_utils.ts
diff --git a/src/legacy/ui/public/vis/components/tooltip/_collect_branch.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_collect_branch.js
similarity index 100%
rename from src/legacy/ui/public/vis/components/tooltip/_collect_branch.js
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_collect_branch.js
diff --git a/src/legacy/ui/public/vis/components/tooltip/_collect_branch.test.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_collect_branch.test.js
similarity index 100%
rename from src/legacy/ui/public/vis/components/tooltip/_collect_branch.test.js
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_collect_branch.test.js
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_hierarchical_tooltip_formatter.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_hierarchical_tooltip_formatter.js
new file mode 100644
index 0000000000000..22fff9cf6a317
--- /dev/null
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_hierarchical_tooltip_formatter.js
@@ -0,0 +1,93 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import _ from 'lodash';
+import numeral from 'numeral';
+import { renderToStaticMarkup } from 'react-dom/server';
+
+import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
+
+import { collectBranch } from './_collect_branch';
+
+export function hierarchicalTooltipFormatter(metricFieldFormatter) {
+ return function({ datum }) {
+ // Collect the current leaf and parents into an array of values
+ const rows = collectBranch(datum);
+
+ // Map those values to what the tooltipSource.rows format.
+ _.forEachRight(rows, function(row) {
+ row.spacer = _.escape(_.repeat(' ', row.depth));
+
+ let percent;
+ if (row.item.percentOfGroup !== null && row.item.percentOfGroup !== undefined) {
+ percent = row.item.percentOfGroup;
+ }
+
+ row.metric = metricFieldFormatter ? metricFieldFormatter.convert(row.metric) : row.metric;
+
+ if (percent !== null && percent !== undefined) {
+ row.metric += ' (' + numeral(percent).format('0.[00]%') + ')';
+ }
+
+ return row;
+ });
+
+ return renderToStaticMarkup(
+
+
+
+
+
+
+
+ {/* {metricCol.label} */} |
+
+
+
+ {rows.map((row, index) => (
+
+
+
+
+ {row.field}
+
+ |
+
+ {row.bucket}
+ |
+ {row.metric} |
+
+ ))}
+
+
+
+ );
+ };
+}
diff --git a/src/legacy/ui/public/vis/components/tooltip/_index.scss b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_index.scss
similarity index 100%
rename from src/legacy/ui/public/vis/components/tooltip/_index.scss
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_index.scss
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js
new file mode 100644
index 0000000000000..13c9b8024aac3
--- /dev/null
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js
@@ -0,0 +1,79 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import { renderToStaticMarkup } from 'react-dom/server';
+
+export function pointSeriesTooltipFormatter() {
+ return function tooltipFormatter({ datum, data }) {
+ if (!datum) return '';
+
+ const details = [];
+
+ const currentSeries = data.series && data.series.find(serie => serie.rawId === datum.seriesId);
+ const addDetail = (label, value) => details.push({ label, value });
+
+ if (datum.extraMetrics) {
+ datum.extraMetrics.forEach(metric => {
+ addDetail(metric.label, metric.value);
+ });
+ }
+
+ if (datum.x !== null && datum.x !== undefined) {
+ addDetail(data.xAxisLabel, data.xAxisFormatter(datum.x));
+ }
+
+ if (datum.y !== null && datum.y !== undefined) {
+ const value = datum.yScale ? datum.yScale * datum.y : datum.y;
+ addDetail(currentSeries.label, currentSeries.yAxisFormatter(value));
+ }
+
+ if (datum.z !== null && datum.z !== undefined) {
+ addDetail(currentSeries.zLabel, currentSeries.zAxisFormatter(datum.z));
+ }
+ if (datum.series && datum.parent) {
+ const dimension = datum.parent;
+ addDetail(dimension.title, datum.series);
+ }
+ if (datum.tableRaw) {
+ addDetail(datum.tableRaw.title, datum.tableRaw.value);
+ }
+
+ return renderToStaticMarkup(
+
+
+ {details.map((detail, index) => (
+
+
+ {detail.label}
+ |
+
+
+
+ {detail.value}
+ {detail.percent && ({detail.percent})}
+
+ |
+
+ ))}
+
+
+ );
+ };
+}
diff --git a/src/legacy/ui/public/vis/components/tooltip/_tooltip.scss b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_tooltip.scss
similarity index 97%
rename from src/legacy/ui/public/vis/components/tooltip/_tooltip.scss
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_tooltip.scss
index 5a30ded7c2e5b..bafec7edf3b94 100644
--- a/src/legacy/ui/public/vis/components/tooltip/_tooltip.scss
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_tooltip.scss
@@ -28,7 +28,7 @@
}
.visTooltip__header {
- margin: 0 0 $euiSizeS 0;
+ margin: 0 0 $euiSizeS;
padding: $euiSizeXS $euiSizeS;
display: flex;
align-items: center;
@@ -41,6 +41,7 @@
margin-top: $euiSizeS;
}
}
+
.visTooltip__labelContainer,
.visTooltip__valueContainer {
overflow-wrap: break-word;
diff --git a/src/legacy/ui/public/vis/components/tooltip/index.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/index.js
similarity index 80%
rename from src/legacy/ui/public/vis/components/tooltip/index.js
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/index.js
index e596a8af86533..e394981125a1f 100644
--- a/src/legacy/ui/public/vis/components/tooltip/index.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/index.js
@@ -17,4 +17,6 @@
* under the License.
*/
-export { Tooltip, TooltipProvider } from './tooltip';
+export { Tooltip } from './tooltip';
+export { hierarchicalTooltipFormatter } from './_hierarchical_tooltip_formatter';
+export { pointSeriesTooltipFormatter } from './_pointseries_tooltip_formatter';
diff --git a/src/legacy/ui/public/vis/components/tooltip/position_tooltip.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/position_tooltip.js
similarity index 100%
rename from src/legacy/ui/public/vis/components/tooltip/position_tooltip.js
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/position_tooltip.js
diff --git a/src/legacy/ui/public/vis/components/tooltip/tooltip.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/tooltip.js
similarity index 97%
rename from src/legacy/ui/public/vis/components/tooltip/tooltip.js
rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/tooltip.js
index 764ab24da6740..f7d29164eec6f 100644
--- a/src/legacy/ui/public/vis/components/tooltip/tooltip.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/tooltip.js
@@ -19,9 +19,10 @@
import d3 from 'd3';
import _ from 'lodash';
-import { Binder } from '../../../binder';
-import { positionTooltip } from './position_tooltip';
import $ from 'jquery';
+
+import { Binder } from '../../../legacy_imports';
+import { positionTooltip } from './position_tooltip';
import theme from '@elastic/eui/dist/eui_theme_light.json';
let allContents = [];
@@ -217,7 +218,7 @@ Tooltip.prototype.render = function() {
return content.id !== id;
});
- if (html) allContents.push({ id: id, html: html, order: order });
+ if (html) allContents.push({ id, html, order });
const allHtml = _(allContents)
.sortBy('order')
@@ -253,7 +254,3 @@ Tooltip.prototype.destroy = function() {
this.hide();
this.binder.destroy();
};
-
-export function TooltipProvider() {
- return Tooltip;
-}
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/chart_title.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/chart_title.js
index 1c84f98614b05..3caaf99cbb5c1 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/chart_title.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/chart_title.js
@@ -21,7 +21,7 @@ import d3 from 'd3';
import _ from 'lodash';
import { ErrorHandler } from './_error_handler';
-import { Tooltip } from '../../legacy_imports';
+import { Tooltip } from '../components/tooltip';
export class ChartTitle extends ErrorHandler {
constructor(visConfig) {
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/point_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/point_series.js
index eab3bc02f4eec..ebaf64874d729 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/point_series.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/point_series.js
@@ -229,7 +229,7 @@ export const vislibPointSeriesTypes = {
const tooManySeries =
defaults.charts.length && defaults.charts[0].series.length > cfg.heatmapMaxBuckets;
if (hasCharts && tooManySeries) {
- defaults.error = i18n.translate('kbnVislibVisTypes.vislib.heatmap.maxBucketsText', {
+ defaults.error = i18n.translate('visTypeVislib.vislib.heatmap.maxBucketsText', {
defaultMessage:
'There are too many series defined ({nr}). The configured maximum is {max}.',
values: {
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/_chart.js
index ac6e8130a846a..a36c7c4774dad 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/_chart.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/_chart.js
@@ -22,7 +22,12 @@ import _ from 'lodash';
import { dataLabel } from '../lib/_data_label';
import { Dispatch } from '../lib/dispatch';
-import { Tooltip, getFormat } from '../../legacy_imports';
+import { getFormat } from '../../legacy_imports';
+import {
+ Tooltip,
+ hierarchicalTooltipFormatter,
+ pointSeriesTooltipFormatter,
+} from '../components/tooltip';
/**
* The Base Class for all visualizations.
@@ -45,8 +50,8 @@ export class Chart {
const fieldFormatter = getFormat(this.handler.data.get('tooltipFormatter'));
const tooltipFormatterProvider =
this.handler.visConfig.get('type') === 'pie'
- ? deps.getHierarchicalTooltipFormatter()
- : deps.getPointSeriesTooltipFormatter();
+ ? hierarchicalTooltipFormatter
+ : pointSeriesTooltipFormatter;
const tooltipFormatter = tooltipFormatterProvider(fieldFormatter);
if (this.handler.visConfig && this.handler.visConfig.get('addTooltip', false)) {
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series.js
index c838c51d34bf5..53f06c79d178c 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series.js
@@ -21,7 +21,7 @@ import d3 from 'd3';
import _ from 'lodash';
import $ from 'jquery';
-import { Tooltip } from '../../legacy_imports';
+import { Tooltip } from '../components/tooltip';
import { Chart } from './_chart';
import { TimeMarker } from './time_marker';
import { seriesTypes } from './point_series/series_types';
@@ -153,18 +153,10 @@ export class PointSeries extends Chart {
.attr('class', 'endzone')
.append('rect')
.attr('class', 'zone')
- .attr('x', function(d) {
- return isHorizontal ? d.x : 0;
- })
- .attr('y', function(d) {
- return isHorizontal ? 0 : d.x;
- })
- .attr('height', function(d) {
- return isHorizontal ? height : d.w;
- })
- .attr('width', function(d) {
- return isHorizontal ? d.w : width;
- });
+ .attr('x', d => (isHorizontal ? d.x : 0))
+ .attr('y', d => (isHorizontal ? 0 : d.x))
+ .attr('height', d => (isHorizontal ? height : d.w))
+ .attr('width', d => (isHorizontal ? d.w : width));
function callPlay(event) {
const boundData = event.target.__data__;
diff --git a/src/legacy/ui/public/vis/_index.scss b/src/legacy/ui/public/vis/_index.scss
index 4fb07557977d3..36d586abdb147 100644
--- a/src/legacy/ui/public/vis/_index.scss
+++ b/src/legacy/ui/public/vis/_index.scss
@@ -1,4 +1,2 @@
-@import './components/index';
@import './editors/index';
@import './map/index';
-@import './vis_types/index';
diff --git a/src/legacy/ui/public/vis/components/_index.scss b/src/legacy/ui/public/vis/components/_index.scss
deleted file mode 100644
index 0d79aa9c458ac..0000000000000
--- a/src/legacy/ui/public/vis/components/_index.scss
+++ /dev/null
@@ -1 +0,0 @@
-@import './tooltip/index';
diff --git a/src/legacy/ui/public/vis/components/tooltip/_hierarchical_tooltip.html b/src/legacy/ui/public/vis/components/tooltip/_hierarchical_tooltip.html
deleted file mode 100644
index 43c4793384bf1..0000000000000
--- a/src/legacy/ui/public/vis/components/tooltip/_hierarchical_tooltip.html
+++ /dev/null
@@ -1,28 +0,0 @@
-
diff --git a/src/legacy/ui/public/vis/components/tooltip/_hierarchical_tooltip_formatter.js b/src/legacy/ui/public/vis/components/tooltip/_hierarchical_tooltip_formatter.js
deleted file mode 100644
index aef7bc3913a49..0000000000000
--- a/src/legacy/ui/public/vis/components/tooltip/_hierarchical_tooltip_formatter.js
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import _ from 'lodash';
-import $ from 'jquery';
-
-import chrome from 'ui/chrome';
-
-import { collectBranch } from './_collect_branch';
-import numeral from 'numeral';
-import template from './_hierarchical_tooltip.html';
-
-export function HierarchicalTooltipFormatterProvider($rootScope, $compile, $sce) {
- const $tooltip = $(template);
- const $tooltipScope = $rootScope.$new();
-
- $compile($tooltip)($tooltipScope);
-
- return function(metricFieldFormatter) {
- return function(event) {
- const datum = event.datum;
-
- // Collect the current leaf and parents into an array of values
- $tooltipScope.rows = collectBranch(datum);
-
- // Map those values to what the tooltipSource.rows format.
- _.forEachRight($tooltipScope.rows, function(row) {
- row.spacer = $sce.trustAsHtml(_.repeat(' ', row.depth));
-
- let percent;
- if (row.item.percentOfGroup != null) {
- percent = row.item.percentOfGroup;
- }
-
- row.metric = metricFieldFormatter ? metricFieldFormatter.convert(row.metric) : row.metric;
-
- if (percent != null) {
- row.metric += ' (' + numeral(percent).format('0.[00]%') + ')';
- }
-
- return row;
- });
-
- $tooltipScope.$apply();
- return $tooltip[0].outerHTML;
- };
- };
-}
-
-let _tooltipFormatter;
-export const getHierarchicalTooltipFormatter = () => {
- if (!_tooltipFormatter) {
- throw new Error('tooltip formatter not initialized');
- }
- return _tooltipFormatter;
-};
-
-export const initializeHierarchicalTooltipFormatter = async () => {
- const $injector = await chrome.dangerouslyGetActiveInjector();
- const Private = $injector.get('Private');
- _tooltipFormatter = Private(HierarchicalTooltipFormatterProvider);
-};
-
-export const setHierarchicalTooltipFormatter = Private => {
- _tooltipFormatter = Private(HierarchicalTooltipFormatterProvider);
-};
diff --git a/src/legacy/ui/public/vis/components/tooltip/_pointseries_tooltip.html b/src/legacy/ui/public/vis/components/tooltip/_pointseries_tooltip.html
deleted file mode 100644
index 9e82739a57f0f..0000000000000
--- a/src/legacy/ui/public/vis/components/tooltip/_pointseries_tooltip.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
- {{detail.label}}
- |
-
- {{detail.value}} ({{detail.percent}})
- |
-
-
-
diff --git a/src/legacy/ui/public/vis/components/tooltip/_pointseries_tooltip_formatter.js b/src/legacy/ui/public/vis/components/tooltip/_pointseries_tooltip_formatter.js
deleted file mode 100644
index 88c9e3d67b4a9..0000000000000
--- a/src/legacy/ui/public/vis/components/tooltip/_pointseries_tooltip_formatter.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import $ from 'jquery';
-
-import chrome from 'ui/chrome';
-
-import template from './_pointseries_tooltip.html';
-
-export function PointSeriesTooltipFormatterProvider($compile, $rootScope) {
- const $tooltipScope = $rootScope.$new();
- const $tooltip = $(template);
- $compile($tooltip)($tooltipScope);
-
- return function() {
- return function tooltipFormatter(event) {
- const data = event.data;
- const datum = event.datum;
- if (!datum) return '';
-
- const details = ($tooltipScope.details = []);
-
- const currentSeries =
- data.series && data.series.find(serie => serie.rawId === datum.seriesId);
- const addDetail = (label, value) => details.push({ label, value });
-
- if (datum.extraMetrics) {
- datum.extraMetrics.forEach(metric => {
- addDetail(metric.label, metric.value);
- });
- }
-
- if (datum.x) {
- addDetail(data.xAxisLabel, data.xAxisFormatter(datum.x));
- }
- if (datum.y) {
- const value = datum.yScale ? datum.yScale * datum.y : datum.y;
- addDetail(currentSeries.label, currentSeries.yAxisFormatter(value));
- }
- if (datum.z) {
- addDetail(currentSeries.zLabel, currentSeries.zAxisFormatter(datum.z));
- }
- if (datum.series && datum.parent) {
- const dimension = datum.parent;
- addDetail(dimension.title, datum.series);
- }
- if (datum.tableRaw) {
- addDetail(datum.tableRaw.title, datum.tableRaw.value);
- }
-
- $tooltipScope.$apply();
- return $tooltip[0].outerHTML;
- };
- };
-}
-
-let _tooltipFormatter;
-export const getPointSeriesTooltipFormatter = () => {
- if (!_tooltipFormatter) {
- throw new Error('tooltip formatter not initialized');
- }
- return _tooltipFormatter;
-};
-
-export const initializePointSeriesTooltipFormatter = async () => {
- const $injector = await chrome.dangerouslyGetActiveInjector();
- const Private = $injector.get('Private');
- _tooltipFormatter = Private(PointSeriesTooltipFormatterProvider);
-};
-
-export const setPointSeriesTooltipFormatter = Private => {
- _tooltipFormatter = Private(PointSeriesTooltipFormatterProvider);
-};
diff --git a/src/legacy/ui/public/vis/vis_types/_index.scss b/src/legacy/ui/public/vis/vis_types/_index.scss
deleted file mode 100644
index 9d86383ec40b2..0000000000000
--- a/src/legacy/ui/public/vis/vis_types/_index.scss
+++ /dev/null
@@ -1,2 +0,0 @@
-@import './vislib_vis_type';
-@import './vislib_vis_legend';
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index c3e46a9c04e1c..88fd8360ec728 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -77,8 +77,6 @@
},
"messages": {
"common.ui.aggResponse.allDocsTitle": "すべてのドキュメント",
- "common.ui.aggResponse.fieldLabel": "フィールド",
- "common.ui.aggResponse.valueLabel": "値",
"common.ui.aggTypes.aggNotValidLabel": "- 無効な集約 -",
"common.ui.aggTypes.aggregateWith.noAggsErrorTooltip": "選択されたフィールドには互換性のある集約がありません。",
"common.ui.aggTypes.aggregateWithLabel": "アグリゲーション:",
@@ -509,13 +507,6 @@
"common.ui.vis.editors.sidebar.tabs.optionsLabel": "オプション",
"common.ui.vis.kibanaMap.leaflet.fitDataBoundsAriaLabel": "データバウンドを合わせる",
"common.ui.vis.kibanaMap.zoomWarning": "ズームレベルが最大に達しました。完全にズームインするには、Elasticsearch と Kibana の {defaultDistribution} にアップグレードしてください。{ems} でより多くのズームレベルが利用できます。または、独自のマップサーバーを構成できます。詳細は { wms } または { configSettings} をご覧ください。",
- "common.ui.vis.visTypes.legend.filterForValueButtonAriaLabel": "値 {legendDataLabel} でフィルタリング",
- "common.ui.vis.visTypes.legend.filterOutValueButtonAriaLabel": "値 {legendDataLabel} を除外",
- "common.ui.vis.visTypes.legend.loadingLabel": "読み込み中…",
- "common.ui.vis.visTypes.legend.setColorScreenReaderDescription": "値 {legendDataLabel} の色を設定",
- "common.ui.vis.visTypes.legend.toggleLegendButtonAriaLabel": "凡例を切り替える",
- "common.ui.vis.visTypes.legend.toggleLegendButtonTitle": "凡例を切り替える",
- "common.ui.vis.visTypes.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}、トグルオプション",
"common.ui.vislib.colormaps.bluesText": "青",
"common.ui.vislib.colormaps.greensText": "緑",
"common.ui.vislib.colormaps.greenToRedText": "緑から赤",
@@ -2483,183 +2474,192 @@
"kbn.visualize.wizard.step1Breadcrumb": "作成",
"kbn.visualize.wizard.step2Breadcrumb": "作成",
"kbn.visualizeTitle": "可視化",
- "kbnVislibVisTypes.area.areaDescription": "折れ線グラフの下の数量を強調します。",
- "kbnVislibVisTypes.area.areaTitle": "エリア",
- "kbnVislibVisTypes.area.countText": "カウント",
- "kbnVislibVisTypes.area.groupTitle": "系列を分割",
- "kbnVislibVisTypes.area.metricsTitle": "Y 軸",
- "kbnVislibVisTypes.area.radiusTitle": "点のサイズ",
- "kbnVislibVisTypes.area.segmentTitle": "X 軸",
- "kbnVislibVisTypes.area.splitTitle": "チャートを分割",
- "kbnVislibVisTypes.area.tabs.metricsAxesTitle": "メトリックと軸",
- "kbnVislibVisTypes.area.tabs.panelSettingsTitle": "パネル設定",
- "kbnVislibVisTypes.axisModes.normalText": "標準",
- "kbnVislibVisTypes.axisModes.percentageText": "パーセンテージ",
- "kbnVislibVisTypes.axisModes.silhouetteText": "シルエット",
- "kbnVislibVisTypes.axisModes.wiggleText": "振動",
- "kbnVislibVisTypes.categoryAxis.rotate.angledText": "傾斜",
- "kbnVislibVisTypes.categoryAxis.rotate.horizontalText": "横",
- "kbnVislibVisTypes.categoryAxis.rotate.verticalText": "縦",
- "kbnVislibVisTypes.chartModes.normalText": "標準",
- "kbnVislibVisTypes.chartModes.stackedText": "スタック",
- "kbnVislibVisTypes.chartTypes.areaText": "エリア",
- "kbnVislibVisTypes.chartTypes.barText": "バー",
- "kbnVislibVisTypes.chartTypes.lineText": "折れ線",
- "kbnVislibVisTypes.controls.colorRanges.errorText": "各範囲は前の範囲よりも大きくなければなりません。",
- "kbnVislibVisTypes.controls.colorSchema.colorSchemaLabel": "カラー図表",
- "kbnVislibVisTypes.controls.colorSchema.howToChangeColorsDescription": "それぞれの色は凡例で変更できます。",
- "kbnVislibVisTypes.controls.colorSchema.resetColorsButtonLabel": "色をリセット",
- "kbnVislibVisTypes.controls.colorSchema.reverseColorSchemaLabel": "図表を反転",
- "kbnVislibVisTypes.controls.gaugeOptions.alignmentLabel": "アラインメント",
- "kbnVislibVisTypes.controls.gaugeOptions.autoExtendRangeLabel": "範囲を自動拡張",
- "kbnVislibVisTypes.controls.gaugeOptions.displayWarningsLabel": "警告を表示",
- "kbnVislibVisTypes.controls.gaugeOptions.extendRangeTooltip": "範囲をデータの最高値に広げます。",
- "kbnVislibVisTypes.controls.gaugeOptions.gaugeTypeLabel": "ゲージタイプ",
- "kbnVislibVisTypes.controls.gaugeOptions.labelsTitle": "ラベル",
- "kbnVislibVisTypes.controls.gaugeOptions.percentageModeLabel": "パーセンテージモード",
- "kbnVislibVisTypes.controls.gaugeOptions.rangesTitle": "範囲",
- "kbnVislibVisTypes.controls.gaugeOptions.showLabelsLabel": "ラベルを表示",
- "kbnVislibVisTypes.controls.gaugeOptions.showLegendLabel": "凡例を表示",
- "kbnVislibVisTypes.controls.gaugeOptions.showScaleLabel": "縮尺を表示",
- "kbnVislibVisTypes.controls.gaugeOptions.styleTitle": "スタイル",
- "kbnVislibVisTypes.controls.gaugeOptions.subTextLabel": "サブラベル",
- "kbnVislibVisTypes.controls.gaugeOptions.switchWarningsTooltip": "警告のオン・オフを切り替えます。オンにすると、すべてのラベルを表示できない際に警告が表示されます。",
- "kbnVislibVisTypes.controls.heatmapOptions.colorLabel": "色",
- "kbnVislibVisTypes.controls.heatmapOptions.colorScaleLabel": "カラースケール",
- "kbnVislibVisTypes.controls.heatmapOptions.colorsNumberLabel": "色の数",
- "kbnVislibVisTypes.controls.heatmapOptions.labelsTitle": "ラベル",
- "kbnVislibVisTypes.controls.heatmapOptions.overwriteAutomaticColorLabel": "自動からーを上書きする",
- "kbnVislibVisTypes.controls.heatmapOptions.percentageModeLabel": "パーセンテージモード",
- "kbnVislibVisTypes.controls.heatmapOptions.rotateLabel": "回転",
- "kbnVislibVisTypes.controls.heatmapOptions.scaleToDataBoundsLabel": "データバウンドに合わせる",
- "kbnVislibVisTypes.controls.heatmapOptions.showLabelsTitle": "ラベルを表示",
- "kbnVislibVisTypes.controls.heatmapOptions.useCustomRangesLabel": "カスタム範囲を使用",
- "kbnVislibVisTypes.controls.pointSeries.categoryAxis.alignLabel": "配置",
- "kbnVislibVisTypes.controls.pointSeries.categoryAxis.filterLabelsLabel": "フィルターラベル",
- "kbnVislibVisTypes.controls.pointSeries.categoryAxis.labelsTitle": "ラベル",
- "kbnVislibVisTypes.controls.pointSeries.categoryAxis.positionLabel": "配置",
- "kbnVislibVisTypes.controls.pointSeries.categoryAxis.showLabel": "表示",
- "kbnVislibVisTypes.controls.pointSeries.categoryAxis.showLabelsLabel": "ラベルを表示",
- "kbnVislibVisTypes.controls.pointSeries.categoryAxis.xAxisTitle": "X 軸",
- "kbnVislibVisTypes.controls.pointSeries.gridAxis.dontShowLabel": "非表示",
- "kbnVislibVisTypes.controls.pointSeries.gridAxis.gridText": "グリッド",
- "kbnVislibVisTypes.controls.pointSeries.gridAxis.xAxisLinesLabel": "X 軸線を表示",
- "kbnVislibVisTypes.controls.pointSeries.gridAxis.yAxisLinesDisabledTooltip": "ヒストグラムに X 軸線は表示できません。",
- "kbnVislibVisTypes.controls.pointSeries.gridAxis.yAxisLinesLabel": "Y 軸線を表示",
- "kbnVislibVisTypes.controls.pointSeries.series.chartTypeLabel": "チャートタイプ",
- "kbnVislibVisTypes.controls.pointSeries.series.lineModeLabel": "線のモード",
- "kbnVislibVisTypes.controls.pointSeries.series.lineWidthLabel": "線の幅",
- "kbnVislibVisTypes.controls.pointSeries.series.metricsTitle": "メトリック",
- "kbnVislibVisTypes.controls.pointSeries.series.modeLabel": "モード",
- "kbnVislibVisTypes.controls.pointSeries.series.newAxisLabel": "新規軸…",
- "kbnVislibVisTypes.controls.pointSeries.series.showDotsLabel": "点を表示",
- "kbnVislibVisTypes.controls.pointSeries.series.showLineLabel": "線を表示",
- "kbnVislibVisTypes.controls.pointSeries.series.valueAxisLabel": "値軸",
- "kbnVislibVisTypes.controls.pointSeries.seriesAccordionAriaLabel": "{agg} オプションを切り替える",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.addButtonTooltip": "Y 軸を追加します",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.customExtentsLabel": "カスタム範囲",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.maxLabel": "最高",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.minErrorMessage": "最低値は最高値よりも低く設定する必要があります",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.minLabel": "最低",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.minNeededScaleText": "ログスケールが選択されている場合、最低値は 0 よりも大きいものである必要があります",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.modeLabel": "モード",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.positionLabel": "配置",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.removeButtonTooltip": "Y 軸を削除します",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.scaleToDataBounds.boundsMargin": "境界マージン",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.scaleToDataBounds.minNeededBoundsMargin": "境界マージンは 0 以上でなければなりません",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.scaleToDataBoundsLabel": "データバウンドに合わせる",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.scaleTypeLabel": "スケールタイプ",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.setAxisExtentsLabel": "軸の範囲の設定",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.showLabel": "表示",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.titleLabel": "タイトル",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.toggleCustomExtendsAriaLabel": "カスタム範囲を切り替える",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.toggleOptionsAriaLabel": "{axisName} オプションを切り替える",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.yAxisTitle": "Y 軸",
- "kbnVislibVisTypes.controls.rangeErrorMessage": "値は {min} と {max} の間でなければなりません",
- "kbnVislibVisTypes.controls.truncateLabel": "切り捨て",
- "kbnVislibVisTypes.controls.vislibBasicOptions.legendPositionLabel": "凡例の配置",
- "kbnVislibVisTypes.controls.vislibBasicOptions.showTooltipLabel": "ツールヒントを表示",
- "kbnVislibVisTypes.editors.heatmap.basicSettingsTitle": "基本設定",
- "kbnVislibVisTypes.editors.heatmap.heatmapSettingsTitle": "ヒートマップ設定",
- "kbnVislibVisTypes.editors.heatmap.highlightLabel": "ハイライト範囲",
- "kbnVislibVisTypes.vislib.heatmap.maxBucketsText": "定義された数列が多すぎます ({nr}).構成されている最高値は {max} です。",
- "kbnVislibVisTypes.editors.heatmap.highlightLabelTooltip": "チャートのカーソルを当てた部分と凡例の対応するラベルをハイライトします。",
- "kbnVislibVisTypes.editors.pie.donutLabel": "ドーナッツ",
- "kbnVislibVisTypes.editors.pie.labelsSettingsTitle": "ラベル設定",
- "kbnVislibVisTypes.editors.pie.pieSettingsTitle": "パイ設定",
- "kbnVislibVisTypes.editors.pie.showLabelsLabel": "ラベルを表示",
- "kbnVislibVisTypes.editors.pie.showTopLevelOnlyLabel": "トップレベルのみ表示",
- "kbnVislibVisTypes.editors.pie.showValuesLabel": "値を表示",
- "kbnVislibVisTypes.editors.pointSeries.currentTimeMarkerLabel": "現在時刻マーカー",
- "kbnVislibVisTypes.editors.pointSeries.orderBucketsBySumLabel": "バケットを合計で並べ替え",
- "kbnVislibVisTypes.editors.pointSeries.settingsTitle": "設定",
- "kbnVislibVisTypes.editors.pointSeries.showLabels": "チャートに値を表示",
- "kbnVislibVisTypes.editors.pointSeries.thresholdLine.colorLabel": "ラインカラー",
- "kbnVislibVisTypes.editors.pointSeries.thresholdLine.showLabel": "しきい線を表示",
- "kbnVislibVisTypes.editors.pointSeries.thresholdLine.styleLabel": "ラインスタイル",
- "kbnVislibVisTypes.editors.pointSeries.thresholdLine.valueLabel": "しきい値",
- "kbnVislibVisTypes.editors.pointSeries.thresholdLine.widthLabel": "線の幅",
- "kbnVislibVisTypes.editors.pointSeries.thresholdLineSettingsTitle": "しきい線",
- "kbnVislibVisTypes.functions.pie.help": "パイビジュアライゼーション",
- "kbnVislibVisTypes.functions.vislib.help": "Vislib ビジュアライゼーション",
- "kbnVislibVisTypes.gauge.alignmentAutomaticTitle": "自動",
- "kbnVislibVisTypes.gauge.alignmentHorizontalTitle": "横",
- "kbnVislibVisTypes.gauge.alignmentVerticalTitle": "縦",
- "kbnVislibVisTypes.gauge.gaugeDescription": "ゲージはメトリックのステータスを示します。メトリックの値としきい値との関連性を示すのに使用します。",
- "kbnVislibVisTypes.gauge.gaugeTitle": "ゲージ",
- "kbnVislibVisTypes.gauge.gaugeTypes.arcText": "弧形",
- "kbnVislibVisTypes.gauge.gaugeTypes.circleText": "円",
- "kbnVislibVisTypes.gauge.groupTitle": "グループを分割",
- "kbnVislibVisTypes.gauge.metricTitle": "メトリック",
- "kbnVislibVisTypes.goal.goalDescription": "ゴールチャートは、最終目標にどれだけ近いかを示します。",
- "kbnVislibVisTypes.goal.goalTitle": "ゴール",
- "kbnVislibVisTypes.goal.groupTitle": "グループを分割",
- "kbnVislibVisTypes.goal.metricTitle": "メトリック",
- "kbnVislibVisTypes.heatmap.groupTitle": "Y 軸",
- "kbnVislibVisTypes.heatmap.heatmapDescription": "マトリックス内のセルに影をつける。",
- "kbnVislibVisTypes.heatmap.heatmapTitle": "ヒートマップ",
- "kbnVislibVisTypes.heatmap.metricTitle": "値",
- "kbnVislibVisTypes.heatmap.segmentTitle": "X 軸",
- "kbnVislibVisTypes.heatmap.splitTitle": "チャートを分割",
- "kbnVislibVisTypes.histogram.groupTitle": "系列を分割",
- "kbnVislibVisTypes.histogram.histogramDescription": "連続変数を各軸に割り当てる。",
- "kbnVislibVisTypes.histogram.histogramTitle": "縦棒",
- "kbnVislibVisTypes.histogram.metricTitle": "Y 軸",
- "kbnVislibVisTypes.histogram.radiusTitle": "点のサイズ",
- "kbnVislibVisTypes.histogram.segmentTitle": "X 軸",
- "kbnVislibVisTypes.histogram.splitTitle": "チャートを分割",
- "kbnVislibVisTypes.horizontalBar.groupTitle": "系列を分割",
- "kbnVislibVisTypes.horizontalBar.horizontalBarDescription": "連続変数を各軸に割り当てる。",
- "kbnVislibVisTypes.horizontalBar.horizontalBarTitle": "横棒",
- "kbnVislibVisTypes.horizontalBar.metricTitle": "Y 軸",
- "kbnVislibVisTypes.horizontalBar.radiusTitle": "点のサイズ",
- "kbnVislibVisTypes.horizontalBar.segmentTitle": "X 軸",
- "kbnVislibVisTypes.horizontalBar.splitTitle": "チャートを分割",
- "kbnVislibVisTypes.interpolationModes.smoothedText": "スムーズ",
- "kbnVislibVisTypes.interpolationModes.steppedText": "ステップ",
- "kbnVislibVisTypes.interpolationModes.straightText": "直線",
- "kbnVislibVisTypes.legendPositions.bottomText": "一番下",
- "kbnVislibVisTypes.legendPositions.leftText": "左",
- "kbnVislibVisTypes.legendPositions.rightText": "右",
- "kbnVislibVisTypes.legendPositions.topText": "一番上",
- "kbnVislibVisTypes.line.groupTitle": "系列を分割",
- "kbnVislibVisTypes.line.lineDescription": "トレンドを強調します。",
- "kbnVislibVisTypes.line.lineTitle": "折れ線",
- "kbnVislibVisTypes.line.metricTitle": "Y 軸",
- "kbnVislibVisTypes.line.radiusTitle": "点のサイズ",
- "kbnVislibVisTypes.line.segmentTitle": "X 軸",
- "kbnVislibVisTypes.line.splitTitle": "チャートを分割",
- "kbnVislibVisTypes.pie.metricTitle": "サイズのスライス",
- "kbnVislibVisTypes.pie.pieDescription": "全体に対する内訳を表現する。",
- "kbnVislibVisTypes.pie.pieTitle": "パイ",
- "kbnVislibVisTypes.pie.segmentTitle": "スライスの分割",
- "kbnVislibVisTypes.pie.splitTitle": "チャートを分割",
- "kbnVislibVisTypes.scaleTypes.linearText": "直線",
- "kbnVislibVisTypes.scaleTypes.logText": "ログ",
- "kbnVislibVisTypes.scaleTypes.squareRootText": "平方根",
- "kbnVislibVisTypes.thresholdLine.style.dashedText": "鎖線",
- "kbnVislibVisTypes.thresholdLine.style.dotdashedText": "点線",
- "kbnVislibVisTypes.thresholdLine.style.fullText": "完全",
+ "visTypeVislib.area.areaDescription": "折れ線グラフの下の数量を強調します。",
+ "visTypeVislib.area.areaTitle": "エリア",
+ "visTypeVislib.area.countText": "カウント",
+ "visTypeVislib.area.groupTitle": "系列を分割",
+ "visTypeVislib.area.metricsTitle": "Y 軸",
+ "visTypeVislib.area.radiusTitle": "点のサイズ",
+ "visTypeVislib.area.segmentTitle": "X 軸",
+ "visTypeVislib.area.splitTitle": "チャートを分割",
+ "visTypeVislib.area.tabs.metricsAxesTitle": "メトリックと軸",
+ "visTypeVislib.area.tabs.panelSettingsTitle": "パネル設定",
+ "visTypeVislib.axisModes.normalText": "標準",
+ "visTypeVislib.axisModes.percentageText": "パーセンテージ",
+ "visTypeVislib.axisModes.silhouetteText": "シルエット",
+ "visTypeVislib.axisModes.wiggleText": "振動",
+ "visTypeVislib.categoryAxis.rotate.angledText": "傾斜",
+ "visTypeVislib.categoryAxis.rotate.horizontalText": "横",
+ "visTypeVislib.categoryAxis.rotate.verticalText": "縦",
+ "visTypeVislib.chartModes.normalText": "標準",
+ "visTypeVislib.chartModes.stackedText": "スタック",
+ "visTypeVislib.chartTypes.areaText": "エリア",
+ "visTypeVislib.chartTypes.barText": "バー",
+ "visTypeVislib.chartTypes.lineText": "折れ線",
+ "visTypeVislib.controls.colorRanges.errorText": "各範囲は前の範囲よりも大きくなければなりません。",
+ "visTypeVislib.controls.colorSchema.colorSchemaLabel": "カラー図表",
+ "visTypeVislib.controls.colorSchema.howToChangeColorsDescription": "それぞれの色は凡例で変更できます。",
+ "visTypeVislib.controls.colorSchema.resetColorsButtonLabel": "色をリセット",
+ "visTypeVislib.controls.colorSchema.reverseColorSchemaLabel": "図表を反転",
+ "visTypeVislib.controls.gaugeOptions.alignmentLabel": "アラインメント",
+ "visTypeVislib.controls.gaugeOptions.autoExtendRangeLabel": "範囲を自動拡張",
+ "visTypeVislib.controls.gaugeOptions.displayWarningsLabel": "警告を表示",
+ "visTypeVislib.controls.gaugeOptions.extendRangeTooltip": "範囲をデータの最高値に広げます。",
+ "visTypeVislib.controls.gaugeOptions.gaugeTypeLabel": "ゲージタイプ",
+ "visTypeVislib.controls.gaugeOptions.labelsTitle": "ラベル",
+ "visTypeVislib.controls.gaugeOptions.percentageModeLabel": "パーセンテージモード",
+ "visTypeVislib.controls.gaugeOptions.rangesTitle": "範囲",
+ "visTypeVislib.controls.gaugeOptions.showLabelsLabel": "ラベルを表示",
+ "visTypeVislib.controls.gaugeOptions.showLegendLabel": "凡例を表示",
+ "visTypeVislib.controls.gaugeOptions.showScaleLabel": "縮尺を表示",
+ "visTypeVislib.controls.gaugeOptions.styleTitle": "スタイル",
+ "visTypeVislib.controls.gaugeOptions.subTextLabel": "サブラベル",
+ "visTypeVislib.controls.gaugeOptions.switchWarningsTooltip": "警告のオン・オフを切り替えます。オンにすると、すべてのラベルを表示できない際に警告が表示されます。",
+ "visTypeVislib.controls.heatmapOptions.colorLabel": "色",
+ "visTypeVislib.controls.heatmapOptions.colorScaleLabel": "カラースケール",
+ "visTypeVislib.controls.heatmapOptions.colorsNumberLabel": "色の数",
+ "visTypeVislib.controls.heatmapOptions.labelsTitle": "ラベル",
+ "visTypeVislib.controls.heatmapOptions.overwriteAutomaticColorLabel": "自動からーを上書きする",
+ "visTypeVislib.controls.heatmapOptions.percentageModeLabel": "パーセンテージモード",
+ "visTypeVislib.controls.heatmapOptions.rotateLabel": "回転",
+ "visTypeVislib.controls.heatmapOptions.scaleToDataBoundsLabel": "データバウンドに合わせる",
+ "visTypeVislib.controls.heatmapOptions.showLabelsTitle": "ラベルを表示",
+ "visTypeVislib.controls.heatmapOptions.useCustomRangesLabel": "カスタム範囲を使用",
+ "visTypeVislib.controls.pointSeries.categoryAxis.alignLabel": "配置",
+ "visTypeVislib.controls.pointSeries.categoryAxis.filterLabelsLabel": "フィルターラベル",
+ "visTypeVislib.controls.pointSeries.categoryAxis.labelsTitle": "ラベル",
+ "visTypeVislib.controls.pointSeries.categoryAxis.positionLabel": "配置",
+ "visTypeVislib.controls.pointSeries.categoryAxis.showLabel": "表示",
+ "visTypeVislib.controls.pointSeries.categoryAxis.showLabelsLabel": "ラベルを表示",
+ "visTypeVislib.controls.pointSeries.categoryAxis.xAxisTitle": "X 軸",
+ "visTypeVislib.controls.pointSeries.gridAxis.dontShowLabel": "非表示",
+ "visTypeVislib.controls.pointSeries.gridAxis.gridText": "グリッド",
+ "visTypeVislib.controls.pointSeries.gridAxis.xAxisLinesLabel": "X 軸線を表示",
+ "visTypeVislib.controls.pointSeries.gridAxis.yAxisLinesDisabledTooltip": "ヒストグラムに X 軸線は表示できません。",
+ "visTypeVislib.controls.pointSeries.gridAxis.yAxisLinesLabel": "Y 軸線を表示",
+ "visTypeVislib.controls.pointSeries.series.chartTypeLabel": "チャートタイプ",
+ "visTypeVislib.controls.pointSeries.series.lineModeLabel": "線のモード",
+ "visTypeVislib.controls.pointSeries.series.lineWidthLabel": "線の幅",
+ "visTypeVislib.controls.pointSeries.series.metricsTitle": "メトリック",
+ "visTypeVislib.controls.pointSeries.series.modeLabel": "モード",
+ "visTypeVislib.controls.pointSeries.series.newAxisLabel": "新規軸…",
+ "visTypeVislib.controls.pointSeries.series.showDotsLabel": "点を表示",
+ "visTypeVislib.controls.pointSeries.series.showLineLabel": "線を表示",
+ "visTypeVislib.controls.pointSeries.series.valueAxisLabel": "値軸",
+ "visTypeVislib.controls.pointSeries.seriesAccordionAriaLabel": "{agg} オプションを切り替える",
+ "visTypeVislib.controls.pointSeries.valueAxes.addButtonTooltip": "Y 軸を追加します",
+ "visTypeVislib.controls.pointSeries.valueAxes.customExtentsLabel": "カスタム範囲",
+ "visTypeVislib.controls.pointSeries.valueAxes.maxLabel": "最高",
+ "visTypeVislib.controls.pointSeries.valueAxes.minErrorMessage": "最低値は最高値よりも低く設定する必要があります",
+ "visTypeVislib.controls.pointSeries.valueAxes.minLabel": "最低",
+ "visTypeVislib.controls.pointSeries.valueAxes.minNeededScaleText": "ログスケールが選択されている場合、最低値は 0 よりも大きいものである必要があります",
+ "visTypeVislib.controls.pointSeries.valueAxes.modeLabel": "モード",
+ "visTypeVislib.controls.pointSeries.valueAxes.positionLabel": "配置",
+ "visTypeVislib.controls.pointSeries.valueAxes.removeButtonTooltip": "Y 軸を削除します",
+ "visTypeVislib.controls.pointSeries.valueAxes.scaleToDataBounds.boundsMargin": "境界マージン",
+ "visTypeVislib.controls.pointSeries.valueAxes.scaleToDataBounds.minNeededBoundsMargin": "境界マージンは 0 以上でなければなりません",
+ "visTypeVislib.controls.pointSeries.valueAxes.scaleToDataBoundsLabel": "データバウンドに合わせる",
+ "visTypeVislib.controls.pointSeries.valueAxes.scaleTypeLabel": "スケールタイプ",
+ "visTypeVislib.controls.pointSeries.valueAxes.setAxisExtentsLabel": "軸の範囲の設定",
+ "visTypeVislib.controls.pointSeries.valueAxes.showLabel": "表示",
+ "visTypeVislib.controls.pointSeries.valueAxes.titleLabel": "タイトル",
+ "visTypeVislib.controls.pointSeries.valueAxes.toggleCustomExtendsAriaLabel": "カスタム範囲を切り替える",
+ "visTypeVislib.controls.pointSeries.valueAxes.toggleOptionsAriaLabel": "{axisName} オプションを切り替える",
+ "visTypeVislib.controls.pointSeries.valueAxes.yAxisTitle": "Y 軸",
+ "visTypeVislib.controls.rangeErrorMessage": "値は {min} と {max} の間でなければなりません",
+ "visTypeVislib.controls.truncateLabel": "切り捨て",
+ "visTypeVislib.controls.vislibBasicOptions.legendPositionLabel": "凡例の配置",
+ "visTypeVislib.controls.vislibBasicOptions.showTooltipLabel": "ツールヒントを表示",
+ "visTypeVislib.editors.heatmap.basicSettingsTitle": "基本設定",
+ "visTypeVislib.editors.heatmap.heatmapSettingsTitle": "ヒートマップ設定",
+ "visTypeVislib.editors.heatmap.highlightLabel": "ハイライト範囲",
+ "visTypeVislib.vislib.heatmap.maxBucketsText": "定義された数列が多すぎます ({nr}).構成されている最高値は {max} です。",
+ "visTypeVislib.editors.heatmap.highlightLabelTooltip": "チャートのカーソルを当てた部分と凡例の対応するラベルをハイライトします。",
+ "visTypeVislib.editors.pie.donutLabel": "ドーナッツ",
+ "visTypeVislib.editors.pie.labelsSettingsTitle": "ラベル設定",
+ "visTypeVislib.editors.pie.pieSettingsTitle": "パイ設定",
+ "visTypeVislib.editors.pie.showLabelsLabel": "ラベルを表示",
+ "visTypeVislib.editors.pie.showTopLevelOnlyLabel": "トップレベルのみ表示",
+ "visTypeVislib.editors.pie.showValuesLabel": "値を表示",
+ "visTypeVislib.editors.pointSeries.currentTimeMarkerLabel": "現在時刻マーカー",
+ "visTypeVislib.editors.pointSeries.orderBucketsBySumLabel": "バケットを合計で並べ替え",
+ "visTypeVislib.editors.pointSeries.settingsTitle": "設定",
+ "visTypeVislib.editors.pointSeries.showLabels": "チャートに値を表示",
+ "visTypeVislib.editors.pointSeries.thresholdLine.colorLabel": "ラインカラー",
+ "visTypeVislib.editors.pointSeries.thresholdLine.showLabel": "しきい線を表示",
+ "visTypeVislib.editors.pointSeries.thresholdLine.styleLabel": "ラインスタイル",
+ "visTypeVislib.editors.pointSeries.thresholdLine.valueLabel": "しきい値",
+ "visTypeVislib.editors.pointSeries.thresholdLine.widthLabel": "線の幅",
+ "visTypeVislib.editors.pointSeries.thresholdLineSettingsTitle": "しきい線",
+ "visTypeVislib.functions.pie.help": "パイビジュアライゼーション",
+ "visTypeVislib.functions.vislib.help": "Vislib ビジュアライゼーション",
+ "visTypeVislib.gauge.alignmentAutomaticTitle": "自動",
+ "visTypeVislib.gauge.alignmentHorizontalTitle": "横",
+ "visTypeVislib.gauge.alignmentVerticalTitle": "縦",
+ "visTypeVislib.gauge.gaugeDescription": "ゲージはメトリックのステータスを示します。メトリックの値としきい値との関連性を示すのに使用します。",
+ "visTypeVislib.gauge.gaugeTitle": "ゲージ",
+ "visTypeVislib.gauge.gaugeTypes.arcText": "弧形",
+ "visTypeVislib.gauge.gaugeTypes.circleText": "円",
+ "visTypeVislib.gauge.groupTitle": "グループを分割",
+ "visTypeVislib.gauge.metricTitle": "メトリック",
+ "visTypeVislib.goal.goalDescription": "ゴールチャートは、最終目標にどれだけ近いかを示します。",
+ "visTypeVislib.goal.goalTitle": "ゴール",
+ "visTypeVislib.goal.groupTitle": "グループを分割",
+ "visTypeVislib.goal.metricTitle": "メトリック",
+ "visTypeVislib.heatmap.groupTitle": "Y 軸",
+ "visTypeVislib.heatmap.heatmapDescription": "マトリックス内のセルに影をつける。",
+ "visTypeVislib.heatmap.heatmapTitle": "ヒートマップ",
+ "visTypeVislib.heatmap.metricTitle": "値",
+ "visTypeVislib.heatmap.segmentTitle": "X 軸",
+ "visTypeVislib.heatmap.splitTitle": "チャートを分割",
+ "visTypeVislib.histogram.groupTitle": "系列を分割",
+ "visTypeVislib.histogram.histogramDescription": "連続変数を各軸に割り当てる。",
+ "visTypeVislib.histogram.histogramTitle": "縦棒",
+ "visTypeVislib.histogram.metricTitle": "Y 軸",
+ "visTypeVislib.histogram.radiusTitle": "点のサイズ",
+ "visTypeVislib.histogram.segmentTitle": "X 軸",
+ "visTypeVislib.histogram.splitTitle": "チャートを分割",
+ "visTypeVislib.horizontalBar.groupTitle": "系列を分割",
+ "visTypeVislib.horizontalBar.horizontalBarDescription": "連続変数を各軸に割り当てる。",
+ "visTypeVislib.horizontalBar.horizontalBarTitle": "横棒",
+ "visTypeVislib.horizontalBar.metricTitle": "Y 軸",
+ "visTypeVislib.horizontalBar.radiusTitle": "点のサイズ",
+ "visTypeVislib.horizontalBar.segmentTitle": "X 軸",
+ "visTypeVislib.horizontalBar.splitTitle": "チャートを分割",
+ "visTypeVislib.interpolationModes.smoothedText": "スムーズ",
+ "visTypeVislib.interpolationModes.steppedText": "ステップ",
+ "visTypeVislib.interpolationModes.straightText": "直線",
+ "visTypeVislib.legendPositions.bottomText": "一番下",
+ "visTypeVislib.legendPositions.leftText": "左",
+ "visTypeVislib.legendPositions.rightText": "右",
+ "visTypeVislib.legendPositions.topText": "一番上",
+ "visTypeVislib.line.groupTitle": "系列を分割",
+ "visTypeVislib.line.lineDescription": "トレンドを強調します。",
+ "visTypeVislib.line.lineTitle": "折れ線",
+ "visTypeVislib.line.metricTitle": "Y 軸",
+ "visTypeVislib.line.radiusTitle": "点のサイズ",
+ "visTypeVislib.line.segmentTitle": "X 軸",
+ "visTypeVislib.line.splitTitle": "チャートを分割",
+ "visTypeVislib.pie.metricTitle": "サイズのスライス",
+ "visTypeVislib.pie.pieDescription": "全体に対する内訳を表現する。",
+ "visTypeVislib.pie.pieTitle": "パイ",
+ "visTypeVislib.pie.segmentTitle": "スライスの分割",
+ "visTypeVislib.pie.splitTitle": "チャートを分割",
+ "visTypeVislib.scaleTypes.linearText": "直線",
+ "visTypeVislib.scaleTypes.logText": "ログ",
+ "visTypeVislib.scaleTypes.squareRootText": "平方根",
+ "visTypeVislib.thresholdLine.style.dashedText": "鎖線",
+ "visTypeVislib.thresholdLine.style.dotdashedText": "点線",
+ "visTypeVislib.thresholdLine.style.fullText": "完全",
+ "visTypeVislib.vislib.tooltip.fieldLabel": "フィールド",
+ "visTypeVislib.vislib.tooltip.valueLabel": "値",
+ "visTypeVislib.vislib.legend.filterForValueButtonAriaLabel": "値 {legendDataLabel} でフィルタリング",
+ "visTypeVislib.vislib.legend.filterOutValueButtonAriaLabel": "値 {legendDataLabel} を除外",
+ "visTypeVislib.vislib.legend.loadingLabel": "読み込み中…",
+ "visTypeVislib.vislib.legend.setColorScreenReaderDescription": "値 {legendDataLabel} の色を設定",
+ "visTypeVislib.vislib.legend.toggleLegendButtonAriaLabel": "凡例を切り替える",
+ "visTypeVislib.vislib.legend.toggleLegendButtonTitle": "凡例を切り替える",
+ "visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}、トグルオプション",
"kibana-react.exitFullScreenButton.exitFullScreenModeButtonLabel": "全画面を終了",
"kibana-react.exitFullScreenButton.fullScreenModeDescription": "ESC キーで全画面モードを終了します。",
"kibana-react.savedObjects.finder.filterButtonLabel": "タイプ",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index c40b8c8d0393c..ad62993d50f06 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -77,8 +77,6 @@
},
"messages": {
"common.ui.aggResponse.allDocsTitle": "所有文档",
- "common.ui.aggResponse.fieldLabel": "字段",
- "common.ui.aggResponse.valueLabel": "值",
"common.ui.aggTypes.aggNotValidLabel": "- 聚合无效 -",
"common.ui.aggTypes.aggregateWith.noAggsErrorTooltip": "选择的字段没有兼容的聚合。",
"common.ui.aggTypes.aggregateWithLabel": "聚合对象",
@@ -509,13 +507,6 @@
"common.ui.vis.editors.sidebar.tabs.optionsLabel": "选项",
"common.ui.vis.kibanaMap.leaflet.fitDataBoundsAriaLabel": "适应数据边界",
"common.ui.vis.kibanaMap.zoomWarning": "已达到缩放级别最大数目。要一直放大,请升级到 Elasticsearch 和 Kibana 的 {defaultDistribution}。您可以通过 {ems} 免费使用其他缩放级别。或者,您可以配置自己的地图服务器。请前往 { wms } 或 { configSettings} 以获取详细信息。",
- "common.ui.vis.visTypes.legend.filterForValueButtonAriaLabel": "筛留值 {legendDataLabel}",
- "common.ui.vis.visTypes.legend.filterOutValueButtonAriaLabel": "筛除值 {legendDataLabel}",
- "common.ui.vis.visTypes.legend.loadingLabel": "正在加载……",
- "common.ui.vis.visTypes.legend.setColorScreenReaderDescription": "为值 {legendDataLabel} 设置颜色",
- "common.ui.vis.visTypes.legend.toggleLegendButtonAriaLabel": "切换图例",
- "common.ui.vis.visTypes.legend.toggleLegendButtonTitle": "切换图例",
- "common.ui.vis.visTypes.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}切换选项",
"common.ui.vislib.colormaps.bluesText": "蓝色",
"common.ui.vislib.colormaps.greensText": "绿色",
"common.ui.vislib.colormaps.greenToRedText": "绿到红",
@@ -2483,183 +2474,192 @@
"kbn.visualize.wizard.step1Breadcrumb": "创建",
"kbn.visualize.wizard.step2Breadcrumb": "创建",
"kbn.visualizeTitle": "可视化",
- "kbnVislibVisTypes.area.areaDescription": "突出折线图下方的数量",
- "kbnVislibVisTypes.area.areaTitle": "面积图",
- "kbnVislibVisTypes.area.countText": "计数",
- "kbnVislibVisTypes.area.groupTitle": "拆分序列",
- "kbnVislibVisTypes.area.metricsTitle": "Y 轴",
- "kbnVislibVisTypes.area.radiusTitle": "点大小",
- "kbnVislibVisTypes.area.segmentTitle": "X 轴",
- "kbnVislibVisTypes.area.splitTitle": "拆分图表",
- "kbnVislibVisTypes.area.tabs.metricsAxesTitle": "指标和轴",
- "kbnVislibVisTypes.area.tabs.panelSettingsTitle": "面板设置",
- "kbnVislibVisTypes.axisModes.normalText": "正常",
- "kbnVislibVisTypes.axisModes.percentageText": "百分比",
- "kbnVislibVisTypes.axisModes.silhouetteText": "剪影",
- "kbnVislibVisTypes.axisModes.wiggleText": "扭动",
- "kbnVislibVisTypes.categoryAxis.rotate.angledText": "带角度",
- "kbnVislibVisTypes.categoryAxis.rotate.horizontalText": "水平",
- "kbnVislibVisTypes.categoryAxis.rotate.verticalText": "垂直",
- "kbnVislibVisTypes.chartModes.normalText": "正常",
- "kbnVislibVisTypes.chartModes.stackedText": "堆叠",
- "kbnVislibVisTypes.chartTypes.areaText": "面积图",
- "kbnVislibVisTypes.chartTypes.barText": "条形图",
- "kbnVislibVisTypes.chartTypes.lineText": "折线图",
- "kbnVislibVisTypes.controls.colorRanges.errorText": "每个范围应大于前一范围。",
- "kbnVislibVisTypes.controls.colorSchema.colorSchemaLabel": "颜色模式",
- "kbnVislibVisTypes.controls.colorSchema.howToChangeColorsDescription": "可以更改图例中的各个颜色。",
- "kbnVislibVisTypes.controls.colorSchema.resetColorsButtonLabel": "重置颜色",
- "kbnVislibVisTypes.controls.colorSchema.reverseColorSchemaLabel": "反转模式",
- "kbnVislibVisTypes.controls.gaugeOptions.alignmentLabel": "对齐方式",
- "kbnVislibVisTypes.controls.gaugeOptions.autoExtendRangeLabel": "自动扩展范围",
- "kbnVislibVisTypes.controls.gaugeOptions.displayWarningsLabel": "显示警告",
- "kbnVislibVisTypes.controls.gaugeOptions.extendRangeTooltip": "将数据范围扩展到最大值。",
- "kbnVislibVisTypes.controls.gaugeOptions.gaugeTypeLabel": "仪表类型",
- "kbnVislibVisTypes.controls.gaugeOptions.labelsTitle": "标签",
- "kbnVislibVisTypes.controls.gaugeOptions.percentageModeLabel": "百分比模式",
- "kbnVislibVisTypes.controls.gaugeOptions.rangesTitle": "范围",
- "kbnVislibVisTypes.controls.gaugeOptions.showLabelsLabel": "显示标签",
- "kbnVislibVisTypes.controls.gaugeOptions.showLegendLabel": "显示图例",
- "kbnVislibVisTypes.controls.gaugeOptions.showScaleLabel": "显示比例",
- "kbnVislibVisTypes.controls.gaugeOptions.styleTitle": "样式",
- "kbnVislibVisTypes.controls.gaugeOptions.subTextLabel": "子标签",
- "kbnVislibVisTypes.controls.gaugeOptions.switchWarningsTooltip": "打开/关闭警告。打开时,如果标签没有全部显示,则显示警告。",
- "kbnVislibVisTypes.controls.heatmapOptions.colorLabel": "颜色",
- "kbnVislibVisTypes.controls.heatmapOptions.colorScaleLabel": "色阶",
- "kbnVislibVisTypes.controls.heatmapOptions.colorsNumberLabel": "颜色个数",
- "kbnVislibVisTypes.controls.heatmapOptions.labelsTitle": "标签",
- "kbnVislibVisTypes.controls.heatmapOptions.overwriteAutomaticColorLabel": "覆盖自动配色",
- "kbnVislibVisTypes.controls.heatmapOptions.percentageModeLabel": "百分比模式",
- "kbnVislibVisTypes.controls.heatmapOptions.rotateLabel": "旋转",
- "kbnVislibVisTypes.controls.heatmapOptions.scaleToDataBoundsLabel": "缩放到数据边界",
- "kbnVislibVisTypes.controls.heatmapOptions.showLabelsTitle": "显示标签",
- "kbnVislibVisTypes.controls.heatmapOptions.useCustomRangesLabel": "使用定制范围",
- "kbnVislibVisTypes.controls.pointSeries.categoryAxis.alignLabel": "对齐",
- "kbnVislibVisTypes.controls.pointSeries.categoryAxis.filterLabelsLabel": "筛选标签",
- "kbnVislibVisTypes.controls.pointSeries.categoryAxis.labelsTitle": "标签",
- "kbnVislibVisTypes.controls.pointSeries.categoryAxis.positionLabel": "位置",
- "kbnVislibVisTypes.controls.pointSeries.categoryAxis.showLabel": "显示",
- "kbnVislibVisTypes.controls.pointSeries.categoryAxis.showLabelsLabel": "显示标签",
- "kbnVislibVisTypes.controls.pointSeries.categoryAxis.xAxisTitle": "X 轴",
- "kbnVislibVisTypes.controls.pointSeries.gridAxis.dontShowLabel": "不显示",
- "kbnVislibVisTypes.controls.pointSeries.gridAxis.gridText": "网格",
- "kbnVislibVisTypes.controls.pointSeries.gridAxis.xAxisLinesLabel": "显示 X 轴线",
- "kbnVislibVisTypes.controls.pointSeries.gridAxis.yAxisLinesDisabledTooltip": "直方图的 X 轴线无法显示。",
- "kbnVislibVisTypes.controls.pointSeries.gridAxis.yAxisLinesLabel": "Y 轴线",
- "kbnVislibVisTypes.controls.pointSeries.series.chartTypeLabel": "图表类型",
- "kbnVislibVisTypes.controls.pointSeries.series.lineModeLabel": "线条模式",
- "kbnVislibVisTypes.controls.pointSeries.series.lineWidthLabel": "线条宽度",
- "kbnVislibVisTypes.controls.pointSeries.series.metricsTitle": "指标",
- "kbnVislibVisTypes.controls.pointSeries.series.modeLabel": "模式",
- "kbnVislibVisTypes.controls.pointSeries.series.newAxisLabel": "新建轴…...",
- "kbnVislibVisTypes.controls.pointSeries.series.showDotsLabel": "显示点线",
- "kbnVislibVisTypes.controls.pointSeries.series.showLineLabel": "显示为线条",
- "kbnVislibVisTypes.controls.pointSeries.series.valueAxisLabel": "值轴",
- "kbnVislibVisTypes.controls.pointSeries.seriesAccordionAriaLabel": "切换 {agg} 选项",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.addButtonTooltip": "添加 Y 轴",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.customExtentsLabel": "定制范围",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.maxLabel": "最大值",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.minErrorMessage": "最小值应小于最大值。",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.minLabel": "最小值",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.minNeededScaleText": "如果选择了对数刻度,最小值必须大于 0。",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.modeLabel": "模式",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.positionLabel": "位置",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.removeButtonTooltip": "移除 Y 轴",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.scaleToDataBounds.boundsMargin": "边界边距",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.scaleToDataBounds.minNeededBoundsMargin": "边界边距必须大于或等于 0。",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.scaleToDataBoundsLabel": "缩放到数据边界",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.scaleTypeLabel": "缩放类型",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.setAxisExtentsLabel": "设置轴范围",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.showLabel": "显示",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.titleLabel": "标题",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.toggleCustomExtendsAriaLabel": "切换定制范围",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.toggleOptionsAriaLabel": "切换 {axisName} 选项",
- "kbnVislibVisTypes.controls.pointSeries.valueAxes.yAxisTitle": "Y 轴",
- "kbnVislibVisTypes.controls.rangeErrorMessage": "值必须是在 {min} 到 {max} 的范围内",
- "kbnVislibVisTypes.controls.truncateLabel": "截断",
- "kbnVislibVisTypes.controls.vislibBasicOptions.legendPositionLabel": "图例位置",
- "kbnVislibVisTypes.controls.vislibBasicOptions.showTooltipLabel": "显示工具提示",
- "kbnVislibVisTypes.editors.heatmap.basicSettingsTitle": "基本设置",
- "kbnVislibVisTypes.editors.heatmap.heatmapSettingsTitle": "热图设置",
- "kbnVislibVisTypes.editors.heatmap.highlightLabel": "高亮范围",
- "kbnVislibVisTypes.vislib.heatmap.maxBucketsText": "定义了过多的序列 ({nr})。配置的最大值为 {max}。",
- "kbnVislibVisTypes.editors.heatmap.highlightLabelTooltip": "高亮显示图表中鼠标悬停的范围以及图例中对应的标签。",
- "kbnVislibVisTypes.editors.pie.donutLabel": "圆环图",
- "kbnVislibVisTypes.editors.pie.labelsSettingsTitle": "标签设置",
- "kbnVislibVisTypes.editors.pie.pieSettingsTitle": "饼图设置",
- "kbnVislibVisTypes.editors.pie.showLabelsLabel": "显示标签",
- "kbnVislibVisTypes.editors.pie.showTopLevelOnlyLabel": "仅显示顶级",
- "kbnVislibVisTypes.editors.pie.showValuesLabel": "显示值",
- "kbnVislibVisTypes.editors.pointSeries.currentTimeMarkerLabel": "当前时间标记",
- "kbnVislibVisTypes.editors.pointSeries.orderBucketsBySumLabel": "按总计值排序存储桶",
- "kbnVislibVisTypes.editors.pointSeries.settingsTitle": "设置",
- "kbnVislibVisTypes.editors.pointSeries.showLabels": "在图表上显示值",
- "kbnVislibVisTypes.editors.pointSeries.thresholdLine.colorLabel": "线条颜色",
- "kbnVislibVisTypes.editors.pointSeries.thresholdLine.showLabel": "显示阈值线条",
- "kbnVislibVisTypes.editors.pointSeries.thresholdLine.styleLabel": "线条样式",
- "kbnVislibVisTypes.editors.pointSeries.thresholdLine.valueLabel": "阈值",
- "kbnVislibVisTypes.editors.pointSeries.thresholdLine.widthLabel": "线条宽度",
- "kbnVislibVisTypes.editors.pointSeries.thresholdLineSettingsTitle": "阈值线条",
- "kbnVislibVisTypes.functions.pie.help": "饼图可视化",
- "kbnVislibVisTypes.functions.vislib.help": "Vislib 可视化",
- "kbnVislibVisTypes.gauge.alignmentAutomaticTitle": "自动",
- "kbnVislibVisTypes.gauge.alignmentHorizontalTitle": "水平",
- "kbnVislibVisTypes.gauge.alignmentVerticalTitle": "垂直",
- "kbnVislibVisTypes.gauge.gaugeDescription": "仪表盘图指示指标的状态。用于显示指标值与参考阈值的相关程度。",
- "kbnVislibVisTypes.gauge.gaugeTitle": "仪表盘图",
- "kbnVislibVisTypes.gauge.gaugeTypes.arcText": "弧形",
- "kbnVislibVisTypes.gauge.gaugeTypes.circleText": "圆形",
- "kbnVislibVisTypes.gauge.groupTitle": "拆分组",
- "kbnVislibVisTypes.gauge.metricTitle": "指标",
- "kbnVislibVisTypes.goal.goalDescription": "目标图指示与最终目标的接近程度。",
- "kbnVislibVisTypes.goal.goalTitle": "目标图",
- "kbnVislibVisTypes.goal.groupTitle": "拆分组",
- "kbnVislibVisTypes.goal.metricTitle": "指标",
- "kbnVislibVisTypes.heatmap.groupTitle": "Y 轴",
- "kbnVislibVisTypes.heatmap.heatmapDescription": "为矩阵中的单元格添加阴影",
- "kbnVislibVisTypes.heatmap.heatmapTitle": "热力图",
- "kbnVislibVisTypes.heatmap.metricTitle": "值",
- "kbnVislibVisTypes.heatmap.segmentTitle": "X 轴",
- "kbnVislibVisTypes.heatmap.splitTitle": "拆分图表",
- "kbnVislibVisTypes.histogram.groupTitle": "拆分序列",
- "kbnVislibVisTypes.histogram.histogramDescription": "向每个轴赋予连续变量",
- "kbnVislibVisTypes.histogram.histogramTitle": "垂直条形图",
- "kbnVislibVisTypes.histogram.metricTitle": "Y 轴",
- "kbnVislibVisTypes.histogram.radiusTitle": "点大小",
- "kbnVislibVisTypes.histogram.segmentTitle": "X 轴",
- "kbnVislibVisTypes.histogram.splitTitle": "拆分图表",
- "kbnVislibVisTypes.horizontalBar.groupTitle": "拆分序列",
- "kbnVislibVisTypes.horizontalBar.horizontalBarDescription": "向每个轴赋予连续变量",
- "kbnVislibVisTypes.horizontalBar.horizontalBarTitle": "水平条形图",
- "kbnVislibVisTypes.horizontalBar.metricTitle": "Y 轴",
- "kbnVislibVisTypes.horizontalBar.radiusTitle": "点大小",
- "kbnVislibVisTypes.horizontalBar.segmentTitle": "X 轴",
- "kbnVislibVisTypes.horizontalBar.splitTitle": "拆分图表",
- "kbnVislibVisTypes.interpolationModes.smoothedText": "平滑",
- "kbnVislibVisTypes.interpolationModes.steppedText": "渐变",
- "kbnVislibVisTypes.interpolationModes.straightText": "直线",
- "kbnVislibVisTypes.legendPositions.bottomText": "下",
- "kbnVislibVisTypes.legendPositions.leftText": "左",
- "kbnVislibVisTypes.legendPositions.rightText": "右",
- "kbnVislibVisTypes.legendPositions.topText": "上",
- "kbnVislibVisTypes.line.groupTitle": "拆分序列",
- "kbnVislibVisTypes.line.lineDescription": "突出趋势",
- "kbnVislibVisTypes.line.lineTitle": "折线图",
- "kbnVislibVisTypes.line.metricTitle": "Y 轴",
- "kbnVislibVisTypes.line.radiusTitle": "点大小",
- "kbnVislibVisTypes.line.segmentTitle": "X 轴",
- "kbnVislibVisTypes.line.splitTitle": "拆分图表",
- "kbnVislibVisTypes.pie.metricTitle": "切片大小",
- "kbnVislibVisTypes.pie.pieDescription": "比较整体的各个部分",
- "kbnVislibVisTypes.pie.pieTitle": "饼图",
- "kbnVislibVisTypes.pie.segmentTitle": "拆分切片",
- "kbnVislibVisTypes.pie.splitTitle": "拆分图表",
- "kbnVislibVisTypes.scaleTypes.linearText": "线性",
- "kbnVislibVisTypes.scaleTypes.logText": "对数",
- "kbnVislibVisTypes.scaleTypes.squareRootText": "平方根",
- "kbnVislibVisTypes.thresholdLine.style.dashedText": "虚线",
- "kbnVislibVisTypes.thresholdLine.style.dotdashedText": "点虚线",
- "kbnVislibVisTypes.thresholdLine.style.fullText": "实线",
+ "visTypeVislib.area.areaDescription": "突出折线图下方的数量",
+ "visTypeVislib.area.areaTitle": "面积图",
+ "visTypeVislib.area.countText": "计数",
+ "visTypeVislib.area.groupTitle": "拆分序列",
+ "visTypeVislib.area.metricsTitle": "Y 轴",
+ "visTypeVislib.area.radiusTitle": "点大小",
+ "visTypeVislib.area.segmentTitle": "X 轴",
+ "visTypeVislib.area.splitTitle": "拆分图表",
+ "visTypeVislib.area.tabs.metricsAxesTitle": "指标和轴",
+ "visTypeVislib.area.tabs.panelSettingsTitle": "面板设置",
+ "visTypeVislib.axisModes.normalText": "正常",
+ "visTypeVislib.axisModes.percentageText": "百分比",
+ "visTypeVislib.axisModes.silhouetteText": "剪影",
+ "visTypeVislib.axisModes.wiggleText": "扭动",
+ "visTypeVislib.categoryAxis.rotate.angledText": "带角度",
+ "visTypeVislib.categoryAxis.rotate.horizontalText": "水平",
+ "visTypeVislib.categoryAxis.rotate.verticalText": "垂直",
+ "visTypeVislib.chartModes.normalText": "正常",
+ "visTypeVislib.chartModes.stackedText": "堆叠",
+ "visTypeVislib.chartTypes.areaText": "面积图",
+ "visTypeVislib.chartTypes.barText": "条形图",
+ "visTypeVislib.chartTypes.lineText": "折线图",
+ "visTypeVislib.controls.colorRanges.errorText": "每个范围应大于前一范围。",
+ "visTypeVislib.controls.colorSchema.colorSchemaLabel": "颜色模式",
+ "visTypeVislib.controls.colorSchema.howToChangeColorsDescription": "可以更改图例中的各个颜色。",
+ "visTypeVislib.controls.colorSchema.resetColorsButtonLabel": "重置颜色",
+ "visTypeVislib.controls.colorSchema.reverseColorSchemaLabel": "反转模式",
+ "visTypeVislib.controls.gaugeOptions.alignmentLabel": "对齐方式",
+ "visTypeVislib.controls.gaugeOptions.autoExtendRangeLabel": "自动扩展范围",
+ "visTypeVislib.controls.gaugeOptions.displayWarningsLabel": "显示警告",
+ "visTypeVislib.controls.gaugeOptions.extendRangeTooltip": "将数据范围扩展到最大值。",
+ "visTypeVislib.controls.gaugeOptions.gaugeTypeLabel": "仪表类型",
+ "visTypeVislib.controls.gaugeOptions.labelsTitle": "标签",
+ "visTypeVislib.controls.gaugeOptions.percentageModeLabel": "百分比模式",
+ "visTypeVislib.controls.gaugeOptions.rangesTitle": "范围",
+ "visTypeVislib.controls.gaugeOptions.showLabelsLabel": "显示标签",
+ "visTypeVislib.controls.gaugeOptions.showLegendLabel": "显示图例",
+ "visTypeVislib.controls.gaugeOptions.showScaleLabel": "显示比例",
+ "visTypeVislib.controls.gaugeOptions.styleTitle": "样式",
+ "visTypeVislib.controls.gaugeOptions.subTextLabel": "子标签",
+ "visTypeVislib.controls.gaugeOptions.switchWarningsTooltip": "打开/关闭警告。打开时,如果标签没有全部显示,则显示警告。",
+ "visTypeVislib.controls.heatmapOptions.colorLabel": "颜色",
+ "visTypeVislib.controls.heatmapOptions.colorScaleLabel": "色阶",
+ "visTypeVislib.controls.heatmapOptions.colorsNumberLabel": "颜色个数",
+ "visTypeVislib.controls.heatmapOptions.labelsTitle": "标签",
+ "visTypeVislib.controls.heatmapOptions.overwriteAutomaticColorLabel": "覆盖自动配色",
+ "visTypeVislib.controls.heatmapOptions.percentageModeLabel": "百分比模式",
+ "visTypeVislib.controls.heatmapOptions.rotateLabel": "旋转",
+ "visTypeVislib.controls.heatmapOptions.scaleToDataBoundsLabel": "缩放到数据边界",
+ "visTypeVislib.controls.heatmapOptions.showLabelsTitle": "显示标签",
+ "visTypeVislib.controls.heatmapOptions.useCustomRangesLabel": "使用定制范围",
+ "visTypeVislib.controls.pointSeries.categoryAxis.alignLabel": "对齐",
+ "visTypeVislib.controls.pointSeries.categoryAxis.filterLabelsLabel": "筛选标签",
+ "visTypeVislib.controls.pointSeries.categoryAxis.labelsTitle": "标签",
+ "visTypeVislib.controls.pointSeries.categoryAxis.positionLabel": "位置",
+ "visTypeVislib.controls.pointSeries.categoryAxis.showLabel": "显示",
+ "visTypeVislib.controls.pointSeries.categoryAxis.showLabelsLabel": "显示标签",
+ "visTypeVislib.controls.pointSeries.categoryAxis.xAxisTitle": "X 轴",
+ "visTypeVislib.controls.pointSeries.gridAxis.dontShowLabel": "不显示",
+ "visTypeVislib.controls.pointSeries.gridAxis.gridText": "网格",
+ "visTypeVislib.controls.pointSeries.gridAxis.xAxisLinesLabel": "显示 X 轴线",
+ "visTypeVislib.controls.pointSeries.gridAxis.yAxisLinesDisabledTooltip": "直方图的 X 轴线无法显示。",
+ "visTypeVislib.controls.pointSeries.gridAxis.yAxisLinesLabel": "Y 轴线",
+ "visTypeVislib.controls.pointSeries.series.chartTypeLabel": "图表类型",
+ "visTypeVislib.controls.pointSeries.series.lineModeLabel": "线条模式",
+ "visTypeVislib.controls.pointSeries.series.lineWidthLabel": "线条宽度",
+ "visTypeVislib.controls.pointSeries.series.metricsTitle": "指标",
+ "visTypeVislib.controls.pointSeries.series.modeLabel": "模式",
+ "visTypeVislib.controls.pointSeries.series.newAxisLabel": "新建轴…...",
+ "visTypeVislib.controls.pointSeries.series.showDotsLabel": "显示点线",
+ "visTypeVislib.controls.pointSeries.series.showLineLabel": "显示为线条",
+ "visTypeVislib.controls.pointSeries.series.valueAxisLabel": "值轴",
+ "visTypeVislib.controls.pointSeries.seriesAccordionAriaLabel": "切换 {agg} 选项",
+ "visTypeVislib.controls.pointSeries.valueAxes.addButtonTooltip": "添加 Y 轴",
+ "visTypeVislib.controls.pointSeries.valueAxes.customExtentsLabel": "定制范围",
+ "visTypeVislib.controls.pointSeries.valueAxes.maxLabel": "最大值",
+ "visTypeVislib.controls.pointSeries.valueAxes.minErrorMessage": "最小值应小于最大值。",
+ "visTypeVislib.controls.pointSeries.valueAxes.minLabel": "最小值",
+ "visTypeVislib.controls.pointSeries.valueAxes.minNeededScaleText": "如果选择了对数刻度,最小值必须大于 0。",
+ "visTypeVislib.controls.pointSeries.valueAxes.modeLabel": "模式",
+ "visTypeVislib.controls.pointSeries.valueAxes.positionLabel": "位置",
+ "visTypeVislib.controls.pointSeries.valueAxes.removeButtonTooltip": "移除 Y 轴",
+ "visTypeVislib.controls.pointSeries.valueAxes.scaleToDataBounds.boundsMargin": "边界边距",
+ "visTypeVislib.controls.pointSeries.valueAxes.scaleToDataBounds.minNeededBoundsMargin": "边界边距必须大于或等于 0。",
+ "visTypeVislib.controls.pointSeries.valueAxes.scaleToDataBoundsLabel": "缩放到数据边界",
+ "visTypeVislib.controls.pointSeries.valueAxes.scaleTypeLabel": "缩放类型",
+ "visTypeVislib.controls.pointSeries.valueAxes.setAxisExtentsLabel": "设置轴范围",
+ "visTypeVislib.controls.pointSeries.valueAxes.showLabel": "显示",
+ "visTypeVislib.controls.pointSeries.valueAxes.titleLabel": "标题",
+ "visTypeVislib.controls.pointSeries.valueAxes.toggleCustomExtendsAriaLabel": "切换定制范围",
+ "visTypeVislib.controls.pointSeries.valueAxes.toggleOptionsAriaLabel": "切换 {axisName} 选项",
+ "visTypeVislib.controls.pointSeries.valueAxes.yAxisTitle": "Y 轴",
+ "visTypeVislib.controls.rangeErrorMessage": "值必须是在 {min} 到 {max} 的范围内",
+ "visTypeVislib.controls.truncateLabel": "截断",
+ "visTypeVislib.controls.vislibBasicOptions.legendPositionLabel": "图例位置",
+ "visTypeVislib.controls.vislibBasicOptions.showTooltipLabel": "显示工具提示",
+ "visTypeVislib.editors.heatmap.basicSettingsTitle": "基本设置",
+ "visTypeVislib.editors.heatmap.heatmapSettingsTitle": "热图设置",
+ "visTypeVislib.editors.heatmap.highlightLabel": "高亮范围",
+ "visTypeVislib.vislib.heatmap.maxBucketsText": "定义了过多的序列 ({nr})。配置的最大值为 {max}。",
+ "visTypeVislib.editors.heatmap.highlightLabelTooltip": "高亮显示图表中鼠标悬停的范围以及图例中对应的标签。",
+ "visTypeVislib.editors.pie.donutLabel": "圆环图",
+ "visTypeVislib.editors.pie.labelsSettingsTitle": "标签设置",
+ "visTypeVislib.editors.pie.pieSettingsTitle": "饼图设置",
+ "visTypeVislib.editors.pie.showLabelsLabel": "显示标签",
+ "visTypeVislib.editors.pie.showTopLevelOnlyLabel": "仅显示顶级",
+ "visTypeVislib.editors.pie.showValuesLabel": "显示值",
+ "visTypeVislib.editors.pointSeries.currentTimeMarkerLabel": "当前时间标记",
+ "visTypeVislib.editors.pointSeries.orderBucketsBySumLabel": "按总计值排序存储桶",
+ "visTypeVislib.editors.pointSeries.settingsTitle": "设置",
+ "visTypeVislib.editors.pointSeries.showLabels": "在图表上显示值",
+ "visTypeVislib.editors.pointSeries.thresholdLine.colorLabel": "线条颜色",
+ "visTypeVislib.editors.pointSeries.thresholdLine.showLabel": "显示阈值线条",
+ "visTypeVislib.editors.pointSeries.thresholdLine.styleLabel": "线条样式",
+ "visTypeVislib.editors.pointSeries.thresholdLine.valueLabel": "阈值",
+ "visTypeVislib.editors.pointSeries.thresholdLine.widthLabel": "线条宽度",
+ "visTypeVislib.editors.pointSeries.thresholdLineSettingsTitle": "阈值线条",
+ "visTypeVislib.functions.pie.help": "饼图可视化",
+ "visTypeVislib.functions.vislib.help": "Vislib 可视化",
+ "visTypeVislib.gauge.alignmentAutomaticTitle": "自动",
+ "visTypeVislib.gauge.alignmentHorizontalTitle": "水平",
+ "visTypeVislib.gauge.alignmentVerticalTitle": "垂直",
+ "visTypeVislib.gauge.gaugeDescription": "仪表盘图指示指标的状态。用于显示指标值与参考阈值的相关程度。",
+ "visTypeVislib.gauge.gaugeTitle": "仪表盘图",
+ "visTypeVislib.gauge.gaugeTypes.arcText": "弧形",
+ "visTypeVislib.gauge.gaugeTypes.circleText": "圆形",
+ "visTypeVislib.gauge.groupTitle": "拆分组",
+ "visTypeVislib.gauge.metricTitle": "指标",
+ "visTypeVislib.goal.goalDescription": "目标图指示与最终目标的接近程度。",
+ "visTypeVislib.goal.goalTitle": "目标图",
+ "visTypeVislib.goal.groupTitle": "拆分组",
+ "visTypeVislib.goal.metricTitle": "指标",
+ "visTypeVislib.heatmap.groupTitle": "Y 轴",
+ "visTypeVislib.heatmap.heatmapDescription": "为矩阵中的单元格添加阴影",
+ "visTypeVislib.heatmap.heatmapTitle": "热力图",
+ "visTypeVislib.heatmap.metricTitle": "值",
+ "visTypeVislib.heatmap.segmentTitle": "X 轴",
+ "visTypeVislib.heatmap.splitTitle": "拆分图表",
+ "visTypeVislib.histogram.groupTitle": "拆分序列",
+ "visTypeVislib.histogram.histogramDescription": "向每个轴赋予连续变量",
+ "visTypeVislib.histogram.histogramTitle": "垂直条形图",
+ "visTypeVislib.histogram.metricTitle": "Y 轴",
+ "visTypeVislib.histogram.radiusTitle": "点大小",
+ "visTypeVislib.histogram.segmentTitle": "X 轴",
+ "visTypeVislib.histogram.splitTitle": "拆分图表",
+ "visTypeVislib.horizontalBar.groupTitle": "拆分序列",
+ "visTypeVislib.horizontalBar.horizontalBarDescription": "向每个轴赋予连续变量",
+ "visTypeVislib.horizontalBar.horizontalBarTitle": "水平条形图",
+ "visTypeVislib.horizontalBar.metricTitle": "Y 轴",
+ "visTypeVislib.horizontalBar.radiusTitle": "点大小",
+ "visTypeVislib.horizontalBar.segmentTitle": "X 轴",
+ "visTypeVislib.horizontalBar.splitTitle": "拆分图表",
+ "visTypeVislib.interpolationModes.smoothedText": "平滑",
+ "visTypeVislib.interpolationModes.steppedText": "渐变",
+ "visTypeVislib.interpolationModes.straightText": "直线",
+ "visTypeVislib.legendPositions.bottomText": "下",
+ "visTypeVislib.legendPositions.leftText": "左",
+ "visTypeVislib.legendPositions.rightText": "右",
+ "visTypeVislib.legendPositions.topText": "上",
+ "visTypeVislib.line.groupTitle": "拆分序列",
+ "visTypeVislib.line.lineDescription": "突出趋势",
+ "visTypeVislib.line.lineTitle": "折线图",
+ "visTypeVislib.line.metricTitle": "Y 轴",
+ "visTypeVislib.line.radiusTitle": "点大小",
+ "visTypeVislib.line.segmentTitle": "X 轴",
+ "visTypeVislib.line.splitTitle": "拆分图表",
+ "visTypeVislib.pie.metricTitle": "切片大小",
+ "visTypeVislib.pie.pieDescription": "比较整体的各个部分",
+ "visTypeVislib.pie.pieTitle": "饼图",
+ "visTypeVislib.pie.segmentTitle": "拆分切片",
+ "visTypeVislib.pie.splitTitle": "拆分图表",
+ "visTypeVislib.scaleTypes.linearText": "线性",
+ "visTypeVislib.scaleTypes.logText": "对数",
+ "visTypeVislib.scaleTypes.squareRootText": "平方根",
+ "visTypeVislib.thresholdLine.style.dashedText": "虚线",
+ "visTypeVislib.thresholdLine.style.dotdashedText": "点虚线",
+ "visTypeVislib.thresholdLine.style.fullText": "实线",
+ "visTypeVislib.vislib.tooltip.fieldLabel": "フィールド",
+ "visTypeVislib.vislib.tooltip.valueLabel": "値",
+ "visTypeVislib.vislib.legend.filterForValueButtonAriaLabel": "筛留值 {legendDataLabel}",
+ "visTypeVislib.vislib.legend.filterOutValueButtonAriaLabel": "筛除值 {legendDataLabel}",
+ "visTypeVislib.vislib.legend.loadingLabel": "正在加载……",
+ "visTypeVislib.vislib.legend.setColorScreenReaderDescription": "为值 {legendDataLabel} 设置颜色",
+ "visTypeVislib.vislib.legend.toggleLegendButtonAriaLabel": "切换图例",
+ "visTypeVislib.vislib.legend.toggleLegendButtonTitle": "切换图例",
+ "visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}切换选项",
"kibana-react.exitFullScreenButton.exitFullScreenModeButtonLabel": "退出全屏",
"kibana-react.exitFullScreenButton.fullScreenModeDescription": "在全屏模式下,按 ESC 键可退出。",
"kibana-react.savedObjects.finder.filterButtonLabel": "类型",
diff --git a/yarn.lock b/yarn.lock
index b534a7a4fcb79..198174d0132fa 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4828,6 +4828,11 @@
resolved "https://registry.yarnpkg.com/@types/numeral/-/numeral-0.0.25.tgz#b6f55062827a4787fe4ab151cf3412a468e65271"
integrity sha512-ShHzHkYD+Ldw3eyttptCpUhF1/mkInWwasQkCNXZHOsJMJ/UMa8wXrxSrTJaVk0r4pLK/VnESVM0wFsfQzNEKQ==
+"@types/numeral@^0.0.26":
+ version "0.0.26"
+ resolved "https://registry.yarnpkg.com/@types/numeral/-/numeral-0.0.26.tgz#cfab9842ef9349ce714b06722940ca7ebf8a6298"
+ integrity sha512-DwCsRqeOWopdEsm5KLTxKVKDSDoj+pzZD1vlwu1GQJ6IF3RhjuleYlRwyRH6MJLGaf3v8wFTnC6wo3yYfz0bnA==
+
"@types/object-hash@^1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@types/object-hash/-/object-hash-1.3.0.tgz#b20db2074129f71829d61ff404e618c4ac3d73cf"
From f26596145ece8e8cb8a5e7fb671bb9953f23eb52 Mon Sep 17 00:00:00 2001
From: Liza Katz
Date: Tue, 21 Jan 2020 18:49:57 +0200
Subject: [PATCH 015/187] Clean up search service (#53766)
* deprecate msearch
* Missing export
* adjust tests, revert loading method of esaggs/boot
* getInjectedMetadata
* Fix jest tests
* update default strategy abort test
* notice update
* Allow running discover errors test independently
* Remove batchSearches
* Detect painless script error
* don't show notifications for aborted requests
* Fix jest tests
* Restore loader indicator
* Decreace loading count on error
* update search test
* Trigger digest after fetching fresh index patterns
* Revert isEqual
* accurate revert
* Return full error details to client from search endpoint
* Re-throw AbortError from http when user aborts request.
* fix typo
* typo
* Adjust routes jest test
* Restore msearch using a separate es connection
* typescript fixes
* set http service mock
* Move es client to dat aplugin, for follow up PR
* Add karma mock
* krma mock
* fix tests
* ts
* Pass in version dynamically
* add headers to esClient host
* Restored fetch soon test
Use tap for loadingCount side effects
* Cleanup search params
* Cleanup search params test
* Revert "Cleanup search params"
This reverts commit ca9dea01d59fddd10b43513450707421248b96d5.
* Revert "Cleanup search params test"
This reverts commit 30b94786122181f41ac44606899655ed85c0dc52.
* Revert code to use old es client until #44302 is resolved
* Revert changes to getPainlessError
* Fix jest test
* Refactor esClient to trigger loadingIndicator
* fixing tests
* use esClient from searchService
* git remove comment
* fix jest
Co-authored-by: Elastic Machine
---
src/core/public/http/fetch.ts | 6 +-
src/legacy/core_plugins/data/public/plugin.ts | 19 +++-
.../build_tabular_inspector_data.ts | 4 +-
.../data/public/search/expressions/esaggs.ts | 10 +-
.../public/search/fetch/call_client.test.ts | 2 +-
.../data/public/search/fetch/call_client.ts | 6 +-
.../data/public/search/fetch/fetch_soon.ts | 10 +-
.../data/public/search/fetch/types.ts | 3 +-
.../search_source/search_source.test.ts | 31 +++++--
.../search/search_source/search_source.ts | 34 ++++---
.../default_search_strategy.test.ts | 44 ++++++---
.../default_search_strategy.ts | 35 ++++++-
.../np_ready/angular/get_painless_error.ts | 3 +-
.../visualizations/public/legacy_imports.ts | 1 -
.../components/visualization_requesterror.tsx | 2 +-
.../public/np_ready/public/index.ts | 2 +
.../new_platform/new_platform.karma_mock.js | 8 ++
src/plugins/data/public/mocks.ts | 17 +++-
src/plugins/data/public/plugin.ts | 21 ++++-
.../create_app_mount_context_search.test.ts | 50 +++++-----
.../search/create_app_mount_context_search.ts | 15 ++-
.../public/search/es_client/get_es_client.ts | 93 +++++++++++++++++++
.../data/public/search/es_client/index.ts | 20 ++++
.../data/public/search/search_service.ts | 22 ++++-
src/plugins/data/public/services.ts | 16 +++-
.../query_string_input.test.tsx.snap | 72 ++++++++++++++
src/plugins/data/server/search/routes.test.ts | 15 ++-
src/plugins/data/server/search/routes.ts | 10 +-
test/functional/apps/discover/_errors.js | 3 +-
.../maps/public/angular/map_controller.js | 4 +-
30 files changed, 474 insertions(+), 104 deletions(-)
rename src/legacy/{ui/public/inspector => core_plugins/data/public/search/expressions}/build_tabular_inspector_data.ts (95%)
create mode 100644 src/plugins/data/public/search/es_client/get_es_client.ts
create mode 100644 src/plugins/data/public/search/es_client/index.ts
diff --git a/src/core/public/http/fetch.ts b/src/core/public/http/fetch.ts
index b86f1f5c08029..b7ceaed6e56a7 100644
--- a/src/core/public/http/fetch.ts
+++ b/src/core/public/http/fetch.ts
@@ -133,7 +133,11 @@ export class Fetch {
try {
response = await window.fetch(request);
} catch (err) {
- throw new HttpFetchError(err.message, request);
+ if (err.name === 'AbortError') {
+ throw err;
+ } else {
+ throw new HttpFetchError(err.message, request);
+ }
}
const contentType = response.headers.get('Content-Type') || '';
diff --git a/src/legacy/core_plugins/data/public/plugin.ts b/src/legacy/core_plugins/data/public/plugin.ts
index 893e477b38583..5329702348207 100644
--- a/src/legacy/core_plugins/data/public/plugin.ts
+++ b/src/legacy/core_plugins/data/public/plugin.ts
@@ -20,15 +20,24 @@
import { CoreSetup, CoreStart, Plugin } from 'kibana/public';
import { SearchService, SearchStart } from './search';
import { DataPublicPluginStart } from '../../../../plugins/data/public';
+import { ExpressionsSetup } from '../../../../plugins/expressions/public';
import {
setFieldFormats,
setNotifications,
setIndexPatterns,
setQueryService,
+ setSearchService,
+ setUiSettings,
+ setInjectedMetadata,
+ setHttp,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../plugins/data/public/services';
+export interface DataPluginSetupDependencies {
+ expressions: ExpressionsSetup;
+}
+
export interface DataPluginStartDependencies {
data: DataPublicPluginStart;
}
@@ -54,18 +63,24 @@ export interface DataStart {
* or static code.
*/
-export class DataPlugin implements Plugin {
+export class DataPlugin
+ implements Plugin {
private readonly search = new SearchService();
- public setup(core: CoreSetup) {}
+ public setup(core: CoreSetup) {
+ setInjectedMetadata(core.injectedMetadata);
+ }
public start(core: CoreStart, { data }: DataPluginStartDependencies): DataStart {
// This is required for when Angular code uses Field and FieldList.
setFieldFormats(data.fieldFormats);
setQueryService(data.query);
+ setSearchService(data.search);
setIndexPatterns(data.indexPatterns);
setFieldFormats(data.fieldFormats);
setNotifications(core.notifications);
+ setUiSettings(core.uiSettings);
+ setHttp(core.http);
return {
search: this.search.start(core),
diff --git a/src/legacy/ui/public/inspector/build_tabular_inspector_data.ts b/src/legacy/core_plugins/data/public/search/expressions/build_tabular_inspector_data.ts
similarity index 95%
rename from src/legacy/ui/public/inspector/build_tabular_inspector_data.ts
rename to src/legacy/core_plugins/data/public/search/expressions/build_tabular_inspector_data.ts
index b09ed60e7186f..6e6d2a15fa2ac 100644
--- a/src/legacy/ui/public/inspector/build_tabular_inspector_data.ts
+++ b/src/legacy/core_plugins/data/public/search/expressions/build_tabular_inspector_data.ts
@@ -19,8 +19,8 @@
import { set } from 'lodash';
// @ts-ignore
-import { createFilter } from '../../../core_plugins/visualizations/public';
-import { FormattedData } from './adapters';
+import { createFilter } from '../../../../visualizations/public';
+import { FormattedData } from '../../../../../../plugins/inspector/public';
interface Column {
id: string;
diff --git a/src/legacy/core_plugins/data/public/search/expressions/esaggs.ts b/src/legacy/core_plugins/data/public/search/expressions/esaggs.ts
index 4ec4dbd7f88d6..889c747c9a62e 100644
--- a/src/legacy/core_plugins/data/public/search/expressions/esaggs.ts
+++ b/src/legacy/core_plugins/data/public/search/expressions/esaggs.ts
@@ -34,14 +34,8 @@ import {
getTime,
FilterManager,
} from '../../../../../../plugins/data/public';
-import {
- SearchSource,
- ISearchSource,
- getRequestInspectorStats,
- getResponseInspectorStats,
-} from '../../../../../ui/public/courier';
-import { buildTabularInspectorData } from '../../../../../ui/public/inspector/build_tabular_inspector_data';
+import { buildTabularInspectorData } from './build_tabular_inspector_data';
import { calculateObjectHash } from '../../../../visualizations/public';
// @ts-ignore
import { tabifyAggResponse } from '../../../../../ui/public/agg_response/tabify/tabify';
@@ -49,6 +43,8 @@ import { PersistedState } from '../../../../../ui/public/persisted_state';
import { Adapters } from '../../../../../../plugins/inspector/public';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { getQueryService, getIndexPatterns } from '../../../../../../plugins/data/public/services';
+import { ISearchSource, getRequestInspectorStats, getResponseInspectorStats } from '../..';
+import { SearchSource } from '../search_source';
export interface RequestHandlerParams {
searchSource: ISearchSource;
diff --git a/src/legacy/core_plugins/data/public/search/fetch/call_client.test.ts b/src/legacy/core_plugins/data/public/search/fetch/call_client.test.ts
index 74c87d77dd4fd..24a36c9db9df7 100644
--- a/src/legacy/core_plugins/data/public/search/fetch/call_client.test.ts
+++ b/src/legacy/core_plugins/data/public/search/fetch/call_client.test.ts
@@ -76,7 +76,7 @@ describe('callClient', () => {
test('Passes the additional arguments it is given to the search strategy', () => {
const searchRequests = [{ _searchStrategyId: 0 }];
- const args = { es: {}, config: {}, esShardTimeout: 0 } as FetchHandlers;
+ const args = { searchService: {}, config: {}, esShardTimeout: 0 } as FetchHandlers;
callClient(searchRequests, [], args);
diff --git a/src/legacy/core_plugins/data/public/search/fetch/call_client.ts b/src/legacy/core_plugins/data/public/search/fetch/call_client.ts
index 43da27f941e4e..ad18775d5f144 100644
--- a/src/legacy/core_plugins/data/public/search/fetch/call_client.ts
+++ b/src/legacy/core_plugins/data/public/search/fetch/call_client.ts
@@ -26,7 +26,7 @@ import { SearchRequest } from '../types';
export function callClient(
searchRequests: SearchRequest[],
requestsOptions: FetchOptions[] = [],
- { es, config, esShardTimeout }: FetchHandlers
+ fetchHandlers: FetchHandlers
) {
// Correlate the options with the request that they're associated with
const requestOptionEntries: Array<[
@@ -53,9 +53,7 @@ export function callClient(
// then an error would have been thrown above
const { searching, abort } = searchStrategy!.search({
searchRequests: requests,
- es,
- config,
- esShardTimeout,
+ ...fetchHandlers,
});
requests.forEach((request, i) => {
diff --git a/src/legacy/core_plugins/data/public/search/fetch/fetch_soon.ts b/src/legacy/core_plugins/data/public/search/fetch/fetch_soon.ts
index 75de85e02a1a2..4830464047ad6 100644
--- a/src/legacy/core_plugins/data/public/search/fetch/fetch_soon.ts
+++ b/src/legacy/core_plugins/data/public/search/fetch/fetch_soon.ts
@@ -28,10 +28,10 @@ import { SearchRequest, SearchResponse } from '../types';
export async function fetchSoon(
request: SearchRequest,
options: FetchOptions,
- { es, config, esShardTimeout }: FetchHandlers
+ fetchHandlers: FetchHandlers
) {
- const msToDelay = config.get('courier:batchSearches') ? 50 : 0;
- return delayedFetch(request, options, { es, config, esShardTimeout }, msToDelay);
+ const msToDelay = fetchHandlers.config.get('courier:batchSearches') ? 50 : 0;
+ return delayedFetch(request, options, fetchHandlers, msToDelay);
}
/**
@@ -64,7 +64,7 @@ let fetchInProgress: Promise | null = null;
async function delayedFetch(
request: SearchRequest,
options: FetchOptions,
- { es, config, esShardTimeout }: FetchHandlers,
+ fetchHandlers: FetchHandlers,
ms: number
) {
const i = requestsToFetch.length;
@@ -73,7 +73,7 @@ async function delayedFetch(
const responses = await (fetchInProgress =
fetchInProgress ||
delay(() => {
- const response = callClient(requestsToFetch, requestOptions, { es, config, esShardTimeout });
+ const response = callClient(requestsToFetch, requestOptions, fetchHandlers);
requestsToFetch = [];
requestOptions = [];
fetchInProgress = null;
diff --git a/src/legacy/core_plugins/data/public/search/fetch/types.ts b/src/legacy/core_plugins/data/public/search/fetch/types.ts
index 0887a1f84c7c8..fba14119d83c3 100644
--- a/src/legacy/core_plugins/data/public/search/fetch/types.ts
+++ b/src/legacy/core_plugins/data/public/search/fetch/types.ts
@@ -17,6 +17,7 @@
* under the License.
*/
+import { ISearchStart } from 'src/plugins/data/public';
import { IUiSettingsClient } from '../../../../../../core/public';
import { SearchRequest, SearchResponse } from '../types';
@@ -35,7 +36,7 @@ export interface FetchOptions {
}
export interface FetchHandlers {
- es: ApiCaller;
+ searchService: ISearchStart;
config: IUiSettingsClient;
esShardTimeout: number;
}
diff --git a/src/legacy/core_plugins/data/public/search/search_source/search_source.test.ts b/src/legacy/core_plugins/data/public/search/search_source/search_source.test.ts
index 28f8dba9a75de..ebeee60b67c8a 100644
--- a/src/legacy/core_plugins/data/public/search/search_source/search_source.test.ts
+++ b/src/legacy/core_plugins/data/public/search/search_source/search_source.test.ts
@@ -19,19 +19,34 @@
import { SearchSource } from '../search_source';
import { IndexPattern } from '../../../../../../plugins/data/public';
-
-jest.mock('ui/new_platform');
+import {
+ setSearchService,
+ setUiSettings,
+ setInjectedMetadata,
+ // eslint-disable-next-line @kbn/eslint/no-restricted-paths
+} from '../../../../../../plugins/data/public/services';
+
+import {
+ injectedMetadataServiceMock,
+ uiSettingsServiceMock,
+} from '../../../../../../core/public/mocks';
+
+setUiSettings(uiSettingsServiceMock.createStartContract());
+setInjectedMetadata(injectedMetadataServiceMock.createSetupContract());
+setSearchService({
+ search: jest.fn(),
+ __LEGACY: {
+ esClient: {
+ search: jest.fn(),
+ msearch: jest.fn(),
+ },
+ },
+});
jest.mock('../fetch', () => ({
fetchSoon: jest.fn().mockResolvedValue({}),
}));
-jest.mock('ui/chrome', () => ({
- dangerouslyGetActiveInjector: () => ({
- get: jest.fn(),
- }),
-}));
-
const getComputedFields = () => ({
storedFields: [],
scriptFields: [],
diff --git a/src/legacy/core_plugins/data/public/search/search_source/search_source.ts b/src/legacy/core_plugins/data/public/search/search_source/search_source.ts
index 6efcae4d4b88d..e977db713ebaa 100644
--- a/src/legacy/core_plugins/data/public/search/search_source/search_source.ts
+++ b/src/legacy/core_plugins/data/public/search/search_source/search_source.ts
@@ -70,8 +70,6 @@
*/
import _ from 'lodash';
-import { npSetup } from 'ui/new_platform';
-import chrome from 'ui/chrome';
import { normalizeSortRequest } from './normalize_sort_request';
import { fetchSoon } from '../fetch';
import { fieldWildcardFilter } from '../../../../../../plugins/kibana_utils/public';
@@ -79,10 +77,14 @@ import { getHighlightRequest, esFilters, esQuery } from '../../../../../../plugi
import { RequestFailure } from '../fetch/errors';
import { filterDocvalueFields } from './filter_docvalue_fields';
import { SearchSourceOptions, SearchSourceFields, SearchRequest } from './types';
-import { FetchOptions, ApiCaller } from '../fetch/types';
+import { FetchOptions } from '../fetch/types';
-const esShardTimeout = npSetup.core.injectedMetadata.getInjectedVar('esShardTimeout') as number;
-const config = npSetup.core.uiSettings;
+import {
+ getSearchService,
+ getUiSettings,
+ getInjectedMetadata,
+ // eslint-disable-next-line @kbn/eslint/no-restricted-paths
+} from '../../../../../../plugins/data/public/services';
export type ISearchSource = Pick;
@@ -192,21 +194,23 @@ export class SearchSource {
* @async
*/
async fetch(options: FetchOptions = {}) {
- const $injector = await chrome.dangerouslyGetActiveInjector();
- const es = $injector.get('es') as ApiCaller;
-
await this.requestIsStarting(options);
const searchRequest = await this.flatten();
this.history = [searchRequest];
+ const esShardTimeout = getInjectedMetadata().getInjectedVar('esShardTimeout') as number;
const response = await fetchSoon(
searchRequest,
{
...(this.searchStrategyId && { searchStrategyId: this.searchStrategyId }),
...options,
},
- { es, config, esShardTimeout }
+ {
+ searchService: getSearchService(),
+ config: getUiSettings(),
+ esShardTimeout,
+ }
);
if (response.error) {
@@ -313,7 +317,11 @@ export class SearchSource {
case 'source':
return addToBody('_source', val);
case 'sort':
- const sort = normalizeSortRequest(val, this.getField('index'), config.get('sort:options'));
+ const sort = normalizeSortRequest(
+ val,
+ this.getField('index'),
+ getUiSettings().get('sort:options')
+ );
return addToBody(key, sort);
default:
return addToBody(key, val);
@@ -359,7 +367,7 @@ export class SearchSource {
if (body._source) {
// exclude source fields for this index pattern specified by the user
- const filter = fieldWildcardFilter(body._source.excludes, config.get('metaFields'));
+ const filter = fieldWildcardFilter(body._source.excludes, getUiSettings().get('metaFields'));
body.docvalue_fields = body.docvalue_fields.filter((docvalueField: any) =>
filter(docvalueField.field)
);
@@ -377,11 +385,11 @@ export class SearchSource {
_.set(body, '_source.includes', remainingFields);
}
- const esQueryConfigs = esQuery.getEsQueryConfig(config);
+ const esQueryConfigs = esQuery.getEsQueryConfig(getUiSettings());
body.query = esQuery.buildEsQuery(index, query, filters, esQueryConfigs);
if (highlightAll && body.query) {
- body.highlight = getHighlightRequest(body.query, config.get('doc_table:highlight'));
+ body.highlight = getHighlightRequest(body.query, getUiSettings().get('doc_table:highlight'));
delete searchRequest.highlightAll;
}
diff --git a/src/legacy/core_plugins/data/public/search/search_strategy/default_search_strategy.test.ts b/src/legacy/core_plugins/data/public/search/search_strategy/default_search_strategy.test.ts
index 0ec6a6c2e143e..8caf20c50cd3a 100644
--- a/src/legacy/core_plugins/data/public/search/search_strategy/default_search_strategy.test.ts
+++ b/src/legacy/core_plugins/data/public/search/search_strategy/default_search_strategy.test.ts
@@ -37,9 +37,16 @@ const searchMockResponse: any = Promise.resolve([]);
searchMockResponse.abort = jest.fn();
const searchMock = jest.fn().mockReturnValue(searchMockResponse);
+const newSearchMockResponse: any = Promise.resolve([]);
+newSearchMockResponse.abort = jest.fn();
+const newSearchMock = jest.fn().mockReturnValue({
+ toPromise: () => searchMockResponse,
+});
+
describe('defaultSearchStrategy', function() {
describe('search', function() {
let searchArgs: MockedKeys>;
+ let es: any;
beforeEach(() => {
msearchMockResponse.abort.mockClear();
@@ -55,17 +62,24 @@ describe('defaultSearchStrategy', function() {
},
],
esShardTimeout: 0,
- es: {
- msearch: msearchMock,
- search: searchMock,
+ searchService: {
+ search: newSearchMock,
+ __LEGACY: {
+ esClient: {
+ search: searchMock,
+ msearch: msearchMock,
+ },
+ },
},
};
+
+ es = searchArgs.searchService.__LEGACY.esClient;
});
test('does not send max_concurrent_shard_requests by default', async () => {
const config = getConfigStub({ 'courier:batchSearches': true });
await search({ ...searchArgs, config });
- expect(searchArgs.es.msearch.mock.calls[0][0].max_concurrent_shard_requests).toBe(undefined);
+ expect(es.msearch.mock.calls[0][0].max_concurrent_shard_requests).toBe(undefined);
});
test('allows configuration of max_concurrent_shard_requests', async () => {
@@ -74,13 +88,13 @@ describe('defaultSearchStrategy', function() {
'courier:maxConcurrentShardRequests': 42,
});
await search({ ...searchArgs, config });
- expect(searchArgs.es.msearch.mock.calls[0][0].max_concurrent_shard_requests).toBe(42);
+ expect(es.msearch.mock.calls[0][0].max_concurrent_shard_requests).toBe(42);
});
test('should set rest_total_hits_as_int to true on a request', async () => {
const config = getConfigStub({ 'courier:batchSearches': true });
await search({ ...searchArgs, config });
- expect(searchArgs.es.msearch.mock.calls[0][0]).toHaveProperty('rest_total_hits_as_int', true);
+ expect(es.msearch.mock.calls[0][0]).toHaveProperty('rest_total_hits_as_int', true);
});
test('should set ignore_throttled=false when including frozen indices', async () => {
@@ -89,7 +103,7 @@ describe('defaultSearchStrategy', function() {
'search:includeFrozen': true,
});
await search({ ...searchArgs, config });
- expect(searchArgs.es.msearch.mock.calls[0][0]).toHaveProperty('ignore_throttled', false);
+ expect(es.msearch.mock.calls[0][0]).toHaveProperty('ignore_throttled', false);
});
test('should properly call abort with msearch', () => {
@@ -100,12 +114,18 @@ describe('defaultSearchStrategy', function() {
expect(msearchMockResponse.abort).toHaveBeenCalled();
});
- test('should properly abort with search', async () => {
- const config = getConfigStub({
- 'courier:batchSearches': false,
- });
+ test('should call new search service', () => {
+ const config = getConfigStub();
+ search({ ...searchArgs, config });
+ expect(searchMock).toHaveBeenCalled();
+ expect(newSearchMock).toHaveBeenCalledTimes(0);
+ });
+
+ test('should properly abort with new search service', async () => {
+ const abortSpy = jest.spyOn(AbortController.prototype, 'abort');
+ const config = getConfigStub({});
search({ ...searchArgs, config }).abort();
- expect(searchMockResponse.abort).toHaveBeenCalled();
+ expect(abortSpy).toHaveBeenCalled();
});
});
});
diff --git a/src/legacy/core_plugins/data/public/search/search_strategy/default_search_strategy.ts b/src/legacy/core_plugins/data/public/search/search_strategy/default_search_strategy.ts
index 9bfa1df71aa81..39789504de0a7 100644
--- a/src/legacy/core_plugins/data/public/search/search_strategy/default_search_strategy.ts
+++ b/src/legacy/core_plugins/data/public/search/search_strategy/default_search_strategy.ts
@@ -38,7 +38,14 @@ export const defaultSearchStrategy: SearchStrategyProvider = {
},
};
-function msearch({ searchRequests, es, config, esShardTimeout }: SearchStrategySearchParams) {
+// @deprecated
+function msearch({
+ searchRequests,
+ searchService,
+ config,
+ esShardTimeout,
+}: SearchStrategySearchParams) {
+ const es = searchService.__LEGACY.esClient;
const inlineRequests = searchRequests.map(({ index, body, search_type: searchType }) => {
const inlineHeader = {
index: index.title || index,
@@ -57,19 +64,39 @@ function msearch({ searchRequests, es, config, esShardTimeout }: SearchStrategyS
...getMSearchParams(config),
body: `${inlineRequests.join('\n')}\n`,
});
+
return {
- searching: searching.then(({ responses }) => responses),
+ searching: searching.then(({ responses }: any) => responses),
abort: searching.abort,
};
}
-function search({ searchRequests, es, config, esShardTimeout }: SearchStrategySearchParams) {
+function search({
+ searchRequests,
+ searchService,
+ config,
+ esShardTimeout,
+}: SearchStrategySearchParams) {
const abortController = new AbortController();
const searchParams = getSearchParams(config, esShardTimeout);
+ const es = searchService.__LEGACY.esClient;
const promises = searchRequests.map(({ index, body }) => {
const searching = es.search({ index: index.title || index, body, ...searchParams });
abortController.signal.addEventListener('abort', searching.abort);
- return searching.catch(({ response }) => JSON.parse(response));
+ return searching.catch(({ response }: any) => JSON.parse(response));
+ /*
+ * Once #44302 is resolved, replace the old implementation with this one -
+ * const params = {
+ * index: index.title || index,
+ * body,
+ * ...searchParams,
+ * };
+ * const { signal } = abortController;
+ * return searchService
+ * .search({ params }, { signal })
+ * .toPromise()
+ * .then(({ rawResponse }) => rawResponse);
+ */
});
return {
searching: Promise.all(promises),
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/get_painless_error.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/get_painless_error.ts
index 212fd870a5aeb..2bbeea9d675c7 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/get_painless_error.ts
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/get_painless_error.ts
@@ -25,6 +25,7 @@ export function getPainlessError(error: Error) {
error,
'resp.error.root_cause'
);
+ const message: string = get(error, 'message');
if (!rootCause) {
return;
@@ -43,6 +44,6 @@ export function getPainlessError(error: Error) {
defaultMessage: "Error with Painless scripted field '{script}'.",
values: { script },
}),
- error: error.message,
+ error: message,
};
}
diff --git a/src/legacy/core_plugins/visualizations/public/legacy_imports.ts b/src/legacy/core_plugins/visualizations/public/legacy_imports.ts
index b750557c24b94..fd40c831ce0ef 100644
--- a/src/legacy/core_plugins/visualizations/public/legacy_imports.ts
+++ b/src/legacy/core_plugins/visualizations/public/legacy_imports.ts
@@ -18,7 +18,6 @@
*/
export { PersistedState } from '../../../ui/public/persisted_state';
-export { SearchError } from '../../../ui/public/courier/search_strategy/search_error';
export { AggConfig } from '../../../ui/public/agg_types/agg_config';
export { AggConfigs } from '../../../ui/public/agg_types/agg_configs';
export {
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_requesterror.tsx b/src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_requesterror.tsx
index 8b9fded919f13..1af9aa3c3e602 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_requesterror.tsx
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/components/visualization_requesterror.tsx
@@ -19,7 +19,7 @@
import { EuiIcon, EuiSpacer, EuiText } from '@elastic/eui';
import React from 'react';
-import { SearchError } from '../../../legacy_imports';
+import { SearchError } from '../../../../../data/public/search/search_strategy';
interface VisualizationRequestErrorProps {
onInit?: () => void;
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts
index 2e9d055858a48..29ff812b95473 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts
@@ -52,3 +52,5 @@ export { buildPipeline, buildVislibDimensions, SchemaConfig } from './legacy/bui
// @ts-ignore
export { updateOldState } from './legacy/vis_update_state';
export { calculateObjectHash } from './legacy/calculate_object_hash';
+// @ts-ignore
+export { createFiltersFromEvent } from './filters/vis_filters';
diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js
index d3f74a540b960..dfd26bc4be039 100644
--- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js
+++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js
@@ -236,6 +236,14 @@ export const npStart = {
history: sinon.fake(),
},
},
+ search: {
+ __LEGACY: {
+ esClient: {
+ search: sinon.fake(),
+ msearch: sinon.fake(),
+ },
+ },
+ },
fieldFormats: getFieldFormatsRegistry(mockCore),
},
share: {
diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts
index 08a7a3ef11537..f44d40f533eed 100644
--- a/src/plugins/data/public/mocks.ts
+++ b/src/plugins/data/public/mocks.ts
@@ -69,13 +69,28 @@ const createStartContract = (): Start => {
const startContract = {
autocomplete: autocompleteMock,
getSuggestions: jest.fn(),
- search: { search: jest.fn() },
+ search: {
+ search: jest.fn(),
+
+ __LEGACY: {
+ esClient: {
+ search: jest.fn(),
+ msearch: jest.fn(),
+ },
+ },
+ },
fieldFormats: fieldFormatsMock as FieldFormatsStart,
query: queryStartMock,
ui: {
IndexPatternSelect: jest.fn(),
SearchBar: jest.fn(),
},
+ __LEGACY: {
+ esClient: {
+ search: jest.fn(),
+ msearch: jest.fn(),
+ },
+ },
indexPatterns: {} as IndexPatternsContract,
};
return startContract;
diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts
index 78abcbcfec467..ce8ca0317bd7d 100644
--- a/src/plugins/data/public/plugin.ts
+++ b/src/plugins/data/public/plugin.ts
@@ -17,7 +17,13 @@
* under the License.
*/
-import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public';
+import {
+ PluginInitializerContext,
+ CoreSetup,
+ CoreStart,
+ Plugin,
+ PackageInfo,
+} from 'src/core/public';
import { Storage, IStorageWrapper } from '../../kibana_utils/public';
import {
DataPublicPluginSetup,
@@ -31,7 +37,13 @@ import { FieldFormatsService } from './field_formats_provider';
import { QueryService } from './query';
import { createIndexPatternSelect } from './ui/index_pattern_select';
import { IndexPatterns } from './index_patterns';
-import { setNotifications, setFieldFormats, setOverlays, setIndexPatterns } from './services';
+import {
+ setNotifications,
+ setFieldFormats,
+ setOverlays,
+ setIndexPatterns,
+ setHttp,
+} from './services';
import { createFilterAction, GLOBAL_APPLY_FILTER_ACTION } from './actions';
import { APPLY_FILTER_TRIGGER } from '../../embeddable/public';
import { createSearchBar } from './ui/search_bar/create_search_bar';
@@ -42,12 +54,14 @@ export class DataPublicPlugin implements Plugin {
it('Returns search fn when there are no strategies', () => {
- const context = createAppMountSearchContext({});
+ const context = createAppMountSearchContext({}, new BehaviorSubject(0));
expect(context.search).toBeDefined();
});
it(`Search throws an error when the strategy doesn't exist`, () => {
- const context = createAppMountSearchContext({});
+ const context = createAppMountSearchContext({}, new BehaviorSubject(0));
expect(() => context.search({}, {}, 'noexist').toPromise()).toThrowErrorMatchingInlineSnapshot(
`"Strategy with name noexist does not exist"`
);
});
it(`Search fn is called on appropriate strategy name`, done => {
- const context = createAppMountSearchContext({
- mysearch: search =>
- Promise.resolve({
- search: () => from(Promise.resolve({ percentComplete: 98 })),
- }),
- anothersearch: search =>
- Promise.resolve({
- search: () => from(Promise.resolve({ percentComplete: 0 })),
- }),
- });
+ const context = createAppMountSearchContext(
+ {
+ mysearch: search =>
+ Promise.resolve({
+ search: () => from(Promise.resolve({ percentComplete: 98 })),
+ }),
+ anothersearch: search =>
+ Promise.resolve({
+ search: () => from(Promise.resolve({ percentComplete: 0 })),
+ }),
+ },
+ new BehaviorSubject(0)
+ );
context.search({}, {}, 'mysearch').subscribe(response => {
expect(response).toEqual({ percentComplete: 98 });
@@ -52,16 +55,19 @@ describe('Create app mount search context', () => {
});
it(`Search fn is called with the passed in request object`, done => {
- const context = createAppMountSearchContext({
- mysearch: search => {
- return Promise.resolve({
- search: request => {
- expect(request).toEqual({ greeting: 'hi' });
- return from(Promise.resolve({}));
- },
- });
+ const context = createAppMountSearchContext(
+ {
+ mysearch: search => {
+ return Promise.resolve({
+ search: request => {
+ expect(request).toEqual({ greeting: 'hi' });
+ return from(Promise.resolve({}));
+ },
+ });
+ },
},
- });
+ new BehaviorSubject(0)
+ );
context.search({ greeting: 'hi' } as any, {}, 'mysearch').subscribe(
response => {},
() => {},
diff --git a/src/plugins/data/public/search/create_app_mount_context_search.ts b/src/plugins/data/public/search/create_app_mount_context_search.ts
index 5659a9c863dc1..f480b8f3e042e 100644
--- a/src/plugins/data/public/search/create_app_mount_context_search.ts
+++ b/src/plugins/data/public/search/create_app_mount_context_search.ts
@@ -17,8 +17,8 @@
* under the License.
*/
-import { mergeMap } from 'rxjs/operators';
-import { from } from 'rxjs';
+import { mergeMap, tap } from 'rxjs/operators';
+import { from, BehaviorSubject } from 'rxjs';
import { ISearchAppMountContext } from './i_search_app_mount_context';
import { ISearchGeneric } from './i_search';
import {
@@ -30,7 +30,8 @@ import { TStrategyTypes } from './strategy_types';
import { DEFAULT_SEARCH_STRATEGY } from '../../common/search';
export const createAppMountSearchContext = (
- searchStrategies: TSearchStrategiesMap
+ searchStrategies: TSearchStrategiesMap,
+ loadingCount$: BehaviorSubject
): ISearchAppMountContext => {
const getSearchStrategy = (
strategyName?: K
@@ -48,7 +49,13 @@ export const createAppMountSearchContext = (
const strategyPromise = getSearchStrategy(strategyName);
return from(strategyPromise).pipe(
mergeMap(strategy => {
- return strategy.search(request, options);
+ loadingCount$.next(loadingCount$.getValue() + 1);
+ return strategy.search(request, options).pipe(
+ tap(
+ error => loadingCount$.next(loadingCount$.getValue() - 1),
+ complete => loadingCount$.next(loadingCount$.getValue() - 1)
+ )
+ );
})
);
};
diff --git a/src/plugins/data/public/search/es_client/get_es_client.ts b/src/plugins/data/public/search/es_client/get_es_client.ts
new file mode 100644
index 0000000000000..6c271643ba012
--- /dev/null
+++ b/src/plugins/data/public/search/es_client/get_es_client.ts
@@ -0,0 +1,93 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// @ts-ignore
+import { default as es } from 'elasticsearch-browser/elasticsearch';
+import { CoreStart, PackageInfo } from 'kibana/public';
+import { BehaviorSubject } from 'rxjs';
+
+export function getEsClient(
+ injectedMetadata: CoreStart['injectedMetadata'],
+ http: CoreStart['http'],
+ packageInfo: PackageInfo,
+ loadingCount$: BehaviorSubject
+) {
+ const esRequestTimeout = injectedMetadata.getInjectedVar('esRequestTimeout') as number;
+ const esApiVersion = injectedMetadata.getInjectedVar('esApiVersion') as string;
+
+ // Use legacy es client for msearch.
+ const client = es.Client({
+ host: getEsUrl(http, packageInfo),
+ log: 'info',
+ requestTimeout: esRequestTimeout,
+ apiVersion: esApiVersion,
+ });
+
+ return {
+ search: wrapEsClientMethod(client, 'search', loadingCount$),
+ msearch: wrapEsClientMethod(client, 'msearch', loadingCount$),
+ create: wrapEsClientMethod(client, 'create', loadingCount$),
+ };
+}
+
+function wrapEsClientMethod(esClient: any, method: string, loadingCount$: BehaviorSubject) {
+ return (args: any) => {
+ // esClient returns a promise, with an additional abort handler
+ // To tap into the abort handling, we have to override that abort handler.
+ const customPromiseThingy = esClient[method](args);
+ const { abort } = customPromiseThingy;
+ let resolved = false;
+
+ // Start LoadingIndicator
+ loadingCount$.next(loadingCount$.getValue() + 1);
+
+ // Stop LoadingIndicator when user aborts
+ customPromiseThingy.abort = () => {
+ abort();
+ if (!resolved) {
+ resolved = true;
+ loadingCount$.next(loadingCount$.getValue() - 1);
+ }
+ };
+
+ // Stop LoadingIndicator when promise finishes
+ customPromiseThingy.finally(() => {
+ resolved = true;
+ loadingCount$.next(loadingCount$.getValue() - 1);
+ });
+
+ return customPromiseThingy;
+ };
+}
+
+function getEsUrl(http: CoreStart['http'], packageInfo: PackageInfo) {
+ const a = document.createElement('a');
+ a.href = http.basePath.prepend('/elasticsearch');
+ const protocolPort = /https/.test(a.protocol) ? 443 : 80;
+ const port = a.port || protocolPort;
+ return {
+ host: a.hostname,
+ port,
+ protocol: a.protocol,
+ pathname: a.pathname,
+ headers: {
+ 'kbn-version': packageInfo.version,
+ },
+ };
+}
diff --git a/src/plugins/data/public/search/es_client/index.ts b/src/plugins/data/public/search/es_client/index.ts
new file mode 100644
index 0000000000000..bf1a3f5d6e7c4
--- /dev/null
+++ b/src/plugins/data/public/search/es_client/index.ts
@@ -0,0 +1,20 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export { getEsClient } from './get_es_client';
diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts
index 6030884c9f6b1..6f3e228939d6d 100644
--- a/src/plugins/data/public/search/search_service.ts
+++ b/src/plugins/data/public/search/search_service.ts
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { BehaviorSubject } from 'rxjs';
import {
Plugin,
CoreSetup,
@@ -23,6 +24,7 @@ import {
CoreStart,
IContextContainer,
PluginOpaqueId,
+ PackageInfo,
} from '../../../../core/public';
import { ISearchAppMountContext } from './i_search_app_mount_context';
@@ -37,6 +39,7 @@ import {
import { TStrategyTypes } from './strategy_types';
import { esSearchService } from './es_search';
import { ISearchGeneric } from './i_search';
+import { getEsClient } from './es_client';
/**
* Extends the AppMountContext so other plugins have access
@@ -50,6 +53,9 @@ declare module 'kibana/public' {
export interface ISearchStart {
search: ISearchGeneric;
+ __LEGACY: {
+ esClient: any;
+ };
}
/**
@@ -74,11 +80,16 @@ export class SearchService implements Plugin {
private contextContainer?: IContextContainer>;
private search?: ISearchGeneric;
+ private readonly loadingCount$ = new BehaviorSubject(0);
constructor(private initializerContext: PluginInitializerContext) {}
public setup(core: CoreSetup): ISearchSetup {
- const search = (this.search = createAppMountSearchContext(this.searchStrategies).search);
+ core.http.addLoadingCountSource(this.loadingCount$);
+ const search = (this.search = createAppMountSearchContext(
+ this.searchStrategies,
+ this.loadingCount$
+ ).search);
core.application.registerMountContext<'search'>('search', () => {
return { search };
});
@@ -115,11 +126,16 @@ export class SearchService implements Plugin {
return api;
}
- public start(core: CoreStart) {
+ public start(core: CoreStart, packageInfo: PackageInfo) {
if (!this.search) {
throw new Error('Search should always be defined');
}
- return { search: this.search };
+ return {
+ search: this.search,
+ __LEGACY: {
+ esClient: getEsClient(core.injectedMetadata, core.http, packageInfo, this.loadingCount$),
+ },
+ };
}
public stop() {}
diff --git a/src/plugins/data/public/services.ts b/src/plugins/data/public/services.ts
index 76b3283220f67..6a15893f573d8 100644
--- a/src/plugins/data/public/services.ts
+++ b/src/plugins/data/public/services.ts
@@ -18,7 +18,7 @@
*/
import { NotificationsStart } from 'src/core/public';
-import { CoreStart } from 'kibana/public';
+import { CoreSetup, CoreStart } from 'kibana/public';
import { FieldFormatsStart } from '.';
import { createGetterSetter } from '../../kibana_utils/public';
import { IndexPatternsContract } from './index_patterns';
@@ -28,6 +28,12 @@ export const [getNotifications, setNotifications] = createGetterSetter(
+ 'UiSettings'
+);
+
+export const [getHttp, setHttp] = createGetterSetter('Http');
+
export const [getFieldFormats, setFieldFormats] = createGetterSetter(
'FieldFormats'
);
@@ -41,3 +47,11 @@ export const [getIndexPatterns, setIndexPatterns] = createGetterSetter('Query');
+
+export const [getInjectedMetadata, setInjectedMetadata] = createGetterSetter<
+ CoreSetup['injectedMetadata']
+>('InjectedMetadata');
+
+export const [getSearchService, setSearchService] = createGetterSetter<
+ DataPublicPluginStart['search']
+>('Search');
diff --git a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
index d8db53d4c6020..f38adff892099 100644
--- a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
+++ b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
@@ -150,6 +150,12 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA
"setIsVisible": [MockFunction],
},
"data": Object {
+ "__LEGACY": Object {
+ "esClient": Object {
+ "msearch": [MockFunction],
+ "search": [MockFunction],
+ },
+ },
"autocomplete": Object {
"getQuerySuggestions": [MockFunction],
"getValueSuggestions": [MockFunction],
@@ -205,6 +211,12 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA
},
},
"search": Object {
+ "__LEGACY": Object {
+ "esClient": Object {
+ "msearch": [MockFunction],
+ "search": [MockFunction],
+ },
+ },
"search": [MockFunction],
},
"ui": Object {
@@ -780,6 +792,12 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA
"setIsVisible": [MockFunction],
},
"data": Object {
+ "__LEGACY": Object {
+ "esClient": Object {
+ "msearch": [MockFunction],
+ "search": [MockFunction],
+ },
+ },
"autocomplete": Object {
"getQuerySuggestions": [MockFunction],
"getValueSuggestions": [MockFunction],
@@ -835,6 +853,12 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA
},
},
"search": Object {
+ "__LEGACY": Object {
+ "esClient": Object {
+ "msearch": [MockFunction],
+ "search": [MockFunction],
+ },
+ },
"search": [MockFunction],
},
"ui": Object {
@@ -1392,6 +1416,12 @@ exports[`QueryStringInput Should pass the query language to the language switche
"setIsVisible": [MockFunction],
},
"data": Object {
+ "__LEGACY": Object {
+ "esClient": Object {
+ "msearch": [MockFunction],
+ "search": [MockFunction],
+ },
+ },
"autocomplete": Object {
"getQuerySuggestions": [MockFunction],
"getValueSuggestions": [MockFunction],
@@ -1447,6 +1477,12 @@ exports[`QueryStringInput Should pass the query language to the language switche
},
},
"search": Object {
+ "__LEGACY": Object {
+ "esClient": Object {
+ "msearch": [MockFunction],
+ "search": [MockFunction],
+ },
+ },
"search": [MockFunction],
},
"ui": Object {
@@ -2019,6 +2055,12 @@ exports[`QueryStringInput Should pass the query language to the language switche
"setIsVisible": [MockFunction],
},
"data": Object {
+ "__LEGACY": Object {
+ "esClient": Object {
+ "msearch": [MockFunction],
+ "search": [MockFunction],
+ },
+ },
"autocomplete": Object {
"getQuerySuggestions": [MockFunction],
"getValueSuggestions": [MockFunction],
@@ -2074,6 +2116,12 @@ exports[`QueryStringInput Should pass the query language to the language switche
},
},
"search": Object {
+ "__LEGACY": Object {
+ "esClient": Object {
+ "msearch": [MockFunction],
+ "search": [MockFunction],
+ },
+ },
"search": [MockFunction],
},
"ui": Object {
@@ -2631,6 +2679,12 @@ exports[`QueryStringInput Should render the given query 1`] = `
"setIsVisible": [MockFunction],
},
"data": Object {
+ "__LEGACY": Object {
+ "esClient": Object {
+ "msearch": [MockFunction],
+ "search": [MockFunction],
+ },
+ },
"autocomplete": Object {
"getQuerySuggestions": [MockFunction],
"getValueSuggestions": [MockFunction],
@@ -2686,6 +2740,12 @@ exports[`QueryStringInput Should render the given query 1`] = `
},
},
"search": Object {
+ "__LEGACY": Object {
+ "esClient": Object {
+ "msearch": [MockFunction],
+ "search": [MockFunction],
+ },
+ },
"search": [MockFunction],
},
"ui": Object {
@@ -3258,6 +3318,12 @@ exports[`QueryStringInput Should render the given query 1`] = `
"setIsVisible": [MockFunction],
},
"data": Object {
+ "__LEGACY": Object {
+ "esClient": Object {
+ "msearch": [MockFunction],
+ "search": [MockFunction],
+ },
+ },
"autocomplete": Object {
"getQuerySuggestions": [MockFunction],
"getValueSuggestions": [MockFunction],
@@ -3313,6 +3379,12 @@ exports[`QueryStringInput Should render the given query 1`] = `
},
},
"search": Object {
+ "__LEGACY": Object {
+ "esClient": Object {
+ "msearch": [MockFunction],
+ "search": [MockFunction],
+ },
+ },
"search": [MockFunction],
},
"ui": Object {
diff --git a/src/plugins/data/server/search/routes.test.ts b/src/plugins/data/server/search/routes.test.ts
index a2394d88f3931..6ea0799f790fc 100644
--- a/src/plugins/data/server/search/routes.test.ts
+++ b/src/plugins/data/server/search/routes.test.ts
@@ -65,8 +65,13 @@ describe('Search service', () => {
expect(mockResponse.ok.mock.calls[0][0]).toEqual({ body: 'yay' });
});
- it('handler throws internal error if the search throws an error', async () => {
- const mockSearch = jest.fn().mockRejectedValue('oh no');
+ it('handler throws an error if the search throws an error', async () => {
+ const mockSearch = jest.fn().mockRejectedValue({
+ message: 'oh no',
+ body: {
+ error: 'oops',
+ },
+ });
const mockContext = {
core: {
elasticsearch: {
@@ -93,7 +98,9 @@ describe('Search service', () => {
expect(mockSearch).toBeCalled();
expect(mockSearch.mock.calls[0][0]).toStrictEqual(mockBody);
expect(mockSearch.mock.calls[0][2]).toBe(mockParams.strategy);
- expect(mockResponse.internalError).toBeCalled();
- expect(mockResponse.internalError.mock.calls[0][0]).toEqual({ body: 'oh no' });
+ expect(mockResponse.customError).toBeCalled();
+ const error: any = mockResponse.customError.mock.calls[0][0];
+ expect(error.body.message).toBe('oh no');
+ expect(error.body.attributes.error).toBe('oops');
});
});
diff --git a/src/plugins/data/server/search/routes.ts b/src/plugins/data/server/search/routes.ts
index eaa72548e08ee..6f726771c41b2 100644
--- a/src/plugins/data/server/search/routes.ts
+++ b/src/plugins/data/server/search/routes.ts
@@ -39,7 +39,15 @@ export function registerSearchRoute(router: IRouter): void {
const response = await context.search!.search(searchRequest, {}, strategy);
return res.ok({ body: response });
} catch (err) {
- return res.internalError({ body: err });
+ return res.customError({
+ statusCode: err.statusCode,
+ body: {
+ message: err.message,
+ attributes: {
+ error: err.body.error,
+ },
+ },
+ });
}
}
);
diff --git a/test/functional/apps/discover/_errors.js b/test/functional/apps/discover/_errors.js
index 53dcd8cc9e5c1..7dbb93c884f46 100644
--- a/test/functional/apps/discover/_errors.js
+++ b/test/functional/apps/discover/_errors.js
@@ -22,10 +22,11 @@ import expect from '@kbn/expect';
export default function({ getService, getPageObjects }) {
const esArchiver = getService('esArchiver');
const testSubjects = getService('testSubjects');
- const PageObjects = getPageObjects(['common']);
+ const PageObjects = getPageObjects(['common', 'discover']);
describe('errors', function describeIndexTests() {
before(async function() {
+ await esArchiver.loadIfNeeded('logstash_functional');
await esArchiver.load('invalid_scripted_field');
await PageObjects.common.navigateToApp('discover');
});
diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js
index 75971d5dfe2a8..eec97dc5c71e9 100644
--- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js
+++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js
@@ -367,7 +367,9 @@ app.controller(
if (prevIndexPatternIds !== nextIndexPatternIds) {
return;
}
- $scope.indexPatterns = indexPatterns;
+ $scope.$evalAsync(() => {
+ $scope.indexPatterns = indexPatterns;
+ });
}
$scope.isFullScreen = false;
From 5aa85dc7516450a8bf5fe5c3a79f51742fb0f5d4 Mon Sep 17 00:00:00 2001
From: Joe Portner <5295965+jportner@users.noreply.github.com>
Date: Tue, 21 Jan 2020 12:35:02 -0500
Subject: [PATCH 016/187] Update Monitoring plugin's Elasticsearch
configuration (#55119)
* Fix Monitoring plugin Elasticsearch SSL config
Plugin now allows "keystore" and "truststore" values in its
config schema as the documentation currently states.
Plugin also now reads PEM and PKCS12 files off of the filesystem
before attempting to create an Elasticsearch client.
* Add missing Elasticsearch config deprecations
Several Elasticsearch config deprecations were overlooked for
monitoring-specific Elasticsearch settings.
---
.../monitoring/__tests__/deprecations.js | 50 +++++
x-pack/legacy/plugins/monitoring/config.js | 8 +
.../legacy/plugins/monitoring/deprecations.js | 26 +++
.../es_client/__tests__/instantiate_client.js | 15 +-
.../server/es_client/instantiate_client.js | 13 +-
.../parse_elasticsearch_config.test.mocks.ts | 15 ++
.../parse_elasticsearch_config.test.ts | 181 ++++++++++++++++++
.../es_client/parse_elasticsearch_config.ts | 111 +++++++++++
.../plugins/monitoring/server/plugin.js | 9 +-
9 files changed, 408 insertions(+), 20 deletions(-)
create mode 100644 x-pack/legacy/plugins/monitoring/server/es_client/parse_elasticsearch_config.test.mocks.ts
create mode 100644 x-pack/legacy/plugins/monitoring/server/es_client/parse_elasticsearch_config.test.ts
create mode 100644 x-pack/legacy/plugins/monitoring/server/es_client/parse_elasticsearch_config.ts
diff --git a/x-pack/legacy/plugins/monitoring/__tests__/deprecations.js b/x-pack/legacy/plugins/monitoring/__tests__/deprecations.js
index aaaf3d6ad40cf..3df93bdb24f32 100644
--- a/x-pack/legacy/plugins/monitoring/__tests__/deprecations.js
+++ b/x-pack/legacy/plugins/monitoring/__tests__/deprecations.js
@@ -80,4 +80,54 @@ describe('monitoring plugin deprecations', function() {
expect(log.called).to.be(true);
});
});
+
+ describe('elasticsearch.username', function() {
+ it('logs a warning if elasticsearch.username is set to "elastic"', () => {
+ const settings = { elasticsearch: { username: 'elastic' } };
+
+ const log = sinon.spy();
+ transformDeprecations(settings, log);
+ expect(log.called).to.be(true);
+ });
+
+ it('does not log a warning if elasticsearch.username is set to something besides "elastic"', () => {
+ const settings = { elasticsearch: { username: 'otheruser' } };
+
+ const log = sinon.spy();
+ transformDeprecations(settings, log);
+ expect(log.called).to.be(false);
+ });
+
+ it('does not log a warning if elasticsearch.username is unset', () => {
+ const settings = { elasticsearch: { username: undefined } };
+
+ const log = sinon.spy();
+ transformDeprecations(settings, log);
+ expect(log.called).to.be(false);
+ });
+
+ it('logs a warning if ssl.key is set and ssl.certificate is not', () => {
+ const settings = { elasticsearch: { ssl: { key: '' } } };
+
+ const log = sinon.spy();
+ transformDeprecations(settings, log);
+ expect(log.called).to.be(true);
+ });
+
+ it('logs a warning if ssl.certificate is set and ssl.key is not', () => {
+ const settings = { elasticsearch: { ssl: { certificate: '' } } };
+
+ const log = sinon.spy();
+ transformDeprecations(settings, log);
+ expect(log.called).to.be(true);
+ });
+
+ it('does not log a warning if both ssl.key and ssl.certificate are set', () => {
+ const settings = { elasticsearch: { ssl: { key: '', certificate: '' } } };
+
+ const log = sinon.spy();
+ transformDeprecations(settings, log);
+ expect(log.called).to.be(false);
+ });
+ });
});
diff --git a/x-pack/legacy/plugins/monitoring/config.js b/x-pack/legacy/plugins/monitoring/config.js
index c33b0b28e830a..91c1ee99a0b2e 100644
--- a/x-pack/legacy/plugins/monitoring/config.js
+++ b/x-pack/legacy/plugins/monitoring/config.js
@@ -83,6 +83,14 @@ export const config = Joi => {
certificate: Joi.string(),
key: Joi.string(),
keyPassphrase: Joi.string(),
+ keystore: Joi.object({
+ path: Joi.string(),
+ password: Joi.string(),
+ }).default(),
+ truststore: Joi.object({
+ path: Joi.string(),
+ password: Joi.string(),
+ }).default(),
alwaysPresentCertificate: Joi.boolean().default(false),
}).default(),
apiVersion: Joi.string().default('master'),
diff --git a/x-pack/legacy/plugins/monitoring/deprecations.js b/x-pack/legacy/plugins/monitoring/deprecations.js
index 13a6a58fa8752..c3b2b70690f33 100644
--- a/x-pack/legacy/plugins/monitoring/deprecations.js
+++ b/x-pack/legacy/plugins/monitoring/deprecations.js
@@ -27,5 +27,31 @@ export const deprecations = () => {
);
}
},
+ (settings, log) => {
+ const fromPath = 'xpack.monitoring.elasticsearch';
+ const es = get(settings, 'elasticsearch');
+ if (es) {
+ if (es.username === 'elastic') {
+ log(
+ `Setting [${fromPath}.username] to "elastic" is deprecated. You should use the "kibana" user instead.`
+ );
+ }
+ }
+ },
+ (settings, log) => {
+ const fromPath = 'xpack.monitoring.elasticsearch.ssl';
+ const ssl = get(settings, 'elasticsearch.ssl');
+ if (ssl) {
+ if (ssl.key !== undefined && ssl.certificate === undefined) {
+ log(
+ `Setting [${fromPath}.key] without [${fromPath}.certificate] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.`
+ );
+ } else if (ssl.certificate !== undefined && ssl.key === undefined) {
+ log(
+ `Setting [${fromPath}.certificate] without [${fromPath}.key] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.`
+ );
+ }
+ }
+ },
];
};
diff --git a/x-pack/legacy/plugins/monitoring/server/es_client/__tests__/instantiate_client.js b/x-pack/legacy/plugins/monitoring/server/es_client/__tests__/instantiate_client.js
index 8797f92e489bb..6844bd5febf8e 100644
--- a/x-pack/legacy/plugins/monitoring/server/es_client/__tests__/instantiate_client.js
+++ b/x-pack/legacy/plugins/monitoring/server/es_client/__tests__/instantiate_client.js
@@ -6,7 +6,7 @@
import expect from '@kbn/expect';
import sinon from 'sinon';
-import { get, noop } from 'lodash';
+import { noop } from 'lodash';
import { exposeClient, hasMonitoringCluster } from '../instantiate_client';
function getMockServerFromConnectionUrl(monitoringClusterUrl) {
@@ -26,15 +26,8 @@ function getMockServerFromConnectionUrl(monitoringClusterUrl) {
},
};
- const config = {
- get: path => {
- return get(server, path);
- },
- set: noop,
- };
-
return {
- config,
+ elasticsearchConfig: server.xpack.monitoring.elasticsearch,
elasticsearchPlugin: {
getCluster: sinon
.stub()
@@ -141,12 +134,12 @@ describe('Instantiate Client', () => {
describe('hasMonitoringCluster', () => {
it('returns true if monitoring is configured', () => {
const server = getMockServerFromConnectionUrl('http://monitoring-cluster.test:9200'); // pass null for URL to create the client using prod config
- expect(hasMonitoringCluster(server.config)).to.be(true);
+ expect(hasMonitoringCluster(server.elasticsearchConfig)).to.be(true);
});
it('returns false if monitoring is not configured', () => {
const server = getMockServerFromConnectionUrl(null);
- expect(hasMonitoringCluster(server.config)).to.be(false);
+ expect(hasMonitoringCluster(server.elasticsearchConfig)).to.be(false);
});
});
});
diff --git a/x-pack/legacy/plugins/monitoring/server/es_client/instantiate_client.js b/x-pack/legacy/plugins/monitoring/server/es_client/instantiate_client.js
index 87a2e5349cf1b..9aed1ac145617 100644
--- a/x-pack/legacy/plugins/monitoring/server/es_client/instantiate_client.js
+++ b/x-pack/legacy/plugins/monitoring/server/es_client/instantiate_client.js
@@ -14,24 +14,21 @@ import { LOGGING_TAG } from '../../common/constants';
* Kibana itself is connected to a production cluster.
*/
-export function exposeClient({ config, events, log, elasticsearchPlugin }) {
- const elasticsearchConfig = hasMonitoringCluster(config)
- ? config.get('xpack.monitoring.elasticsearch')
- : {};
+export function exposeClient({ elasticsearchConfig, events, log, elasticsearchPlugin }) {
+ const isMonitoringCluster = hasMonitoringCluster(elasticsearchConfig);
const cluster = elasticsearchPlugin.createCluster('monitoring', {
- ...elasticsearchConfig,
+ ...(isMonitoringCluster ? elasticsearchConfig : {}),
plugins: [monitoringBulk],
logQueries: Boolean(elasticsearchConfig.logQueries),
});
events.on('stop', bindKey(cluster, 'close'));
- const configSource = hasMonitoringCluster(config) ? 'monitoring' : 'production';
+ const configSource = isMonitoringCluster ? 'monitoring' : 'production';
log([LOGGING_TAG, 'es-client'], `config sourced from: ${configSource} cluster`);
}
export function hasMonitoringCluster(config) {
- const hosts = config.get('xpack.monitoring.elasticsearch.hosts');
- return Boolean(hosts && hosts.length);
+ return Boolean(config.hosts && config.hosts.length);
}
export const instantiateClient = once(exposeClient);
diff --git a/x-pack/legacy/plugins/monitoring/server/es_client/parse_elasticsearch_config.test.mocks.ts b/x-pack/legacy/plugins/monitoring/server/es_client/parse_elasticsearch_config.test.mocks.ts
new file mode 100644
index 0000000000000..42141313ceea2
--- /dev/null
+++ b/x-pack/legacy/plugins/monitoring/server/es_client/parse_elasticsearch_config.test.mocks.ts
@@ -0,0 +1,15 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export const mockReadFileSync = jest.fn();
+jest.mock('fs', () => ({ readFileSync: mockReadFileSync }));
+
+export const mockReadPkcs12Keystore = jest.fn();
+export const mockReadPkcs12Truststore = jest.fn();
+jest.mock('../../../../../../src/core/utils', () => ({
+ readPkcs12Keystore: mockReadPkcs12Keystore,
+ readPkcs12Truststore: mockReadPkcs12Truststore,
+}));
diff --git a/x-pack/legacy/plugins/monitoring/server/es_client/parse_elasticsearch_config.test.ts b/x-pack/legacy/plugins/monitoring/server/es_client/parse_elasticsearch_config.test.ts
new file mode 100644
index 0000000000000..c6f4e0fa68504
--- /dev/null
+++ b/x-pack/legacy/plugins/monitoring/server/es_client/parse_elasticsearch_config.test.ts
@@ -0,0 +1,181 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import {
+ mockReadFileSync,
+ mockReadPkcs12Keystore,
+ mockReadPkcs12Truststore,
+} from './parse_elasticsearch_config.test.mocks';
+
+import { parseElasticsearchConfig } from './parse_elasticsearch_config';
+
+const parse = (config: any) => {
+ return parseElasticsearchConfig({
+ get: () => config,
+ });
+};
+
+describe('reads files', () => {
+ beforeEach(() => {
+ mockReadFileSync.mockReset();
+ mockReadFileSync.mockImplementation((path: string) => `content-of-${path}`);
+ mockReadPkcs12Keystore.mockReset();
+ mockReadPkcs12Keystore.mockImplementation((path: string) => ({
+ key: `content-of-${path}.key`,
+ cert: `content-of-${path}.cert`,
+ ca: [`content-of-${path}.ca`],
+ }));
+ mockReadPkcs12Truststore.mockReset();
+ mockReadPkcs12Truststore.mockImplementation((path: string) => [`content-of-${path}`]);
+ });
+
+ it('reads certificate authorities when ssl.keystore.path is specified', () => {
+ const configValue = parse({ ssl: { keystore: { path: 'some-path' } } });
+ expect(mockReadPkcs12Keystore).toHaveBeenCalledTimes(1);
+ expect(configValue.ssl.certificateAuthorities).toEqual(['content-of-some-path.ca']);
+ });
+
+ it('reads certificate authorities when ssl.truststore.path is specified', () => {
+ const configValue = parse({ ssl: { truststore: { path: 'some-path' } } });
+ expect(mockReadPkcs12Truststore).toHaveBeenCalledTimes(1);
+ expect(configValue.ssl.certificateAuthorities).toEqual(['content-of-some-path']);
+ });
+
+ it('reads certificate authorities when ssl.certificateAuthorities is specified', () => {
+ let configValue = parse({ ssl: { certificateAuthorities: 'some-path' } });
+ expect(mockReadFileSync).toHaveBeenCalledTimes(1);
+ expect(configValue.ssl.certificateAuthorities).toEqual(['content-of-some-path']);
+
+ mockReadFileSync.mockClear();
+ configValue = parse({ ssl: { certificateAuthorities: ['some-path'] } });
+ expect(mockReadFileSync).toHaveBeenCalledTimes(1);
+ expect(configValue.ssl.certificateAuthorities).toEqual(['content-of-some-path']);
+
+ mockReadFileSync.mockClear();
+ configValue = parse({ ssl: { certificateAuthorities: ['some-path', 'another-path'] } });
+ expect(mockReadFileSync).toHaveBeenCalledTimes(2);
+ expect(configValue.ssl.certificateAuthorities).toEqual([
+ 'content-of-some-path',
+ 'content-of-another-path',
+ ]);
+ });
+
+ it('reads certificate authorities when ssl.keystore.path, ssl.truststore.path, and ssl.certificateAuthorities are specified', () => {
+ const configValue = parse({
+ ssl: {
+ keystore: { path: 'some-path' },
+ truststore: { path: 'another-path' },
+ certificateAuthorities: 'yet-another-path',
+ },
+ });
+ expect(mockReadPkcs12Keystore).toHaveBeenCalledTimes(1);
+ expect(mockReadPkcs12Truststore).toHaveBeenCalledTimes(1);
+ expect(mockReadFileSync).toHaveBeenCalledTimes(1);
+ expect(configValue.ssl.certificateAuthorities).toEqual([
+ 'content-of-some-path.ca',
+ 'content-of-another-path',
+ 'content-of-yet-another-path',
+ ]);
+ });
+
+ it('reads a private key and certificate when ssl.keystore.path is specified', () => {
+ const configValue = parse({ ssl: { keystore: { path: 'some-path' } } });
+ expect(mockReadPkcs12Keystore).toHaveBeenCalledTimes(1);
+ expect(configValue.ssl.key).toEqual('content-of-some-path.key');
+ expect(configValue.ssl.certificate).toEqual('content-of-some-path.cert');
+ });
+
+ it('reads a private key when ssl.key is specified', () => {
+ const configValue = parse({ ssl: { key: 'some-path' } });
+ expect(mockReadFileSync).toHaveBeenCalledTimes(1);
+ expect(configValue.ssl.key).toEqual('content-of-some-path');
+ });
+
+ it('reads a certificate when ssl.certificate is specified', () => {
+ const configValue = parse({ ssl: { certificate: 'some-path' } });
+ expect(mockReadFileSync).toHaveBeenCalledTimes(1);
+ expect(configValue.ssl.certificate).toEqual('content-of-some-path');
+ });
+});
+
+describe('throws when config is invalid', () => {
+ beforeAll(() => {
+ const realFs = jest.requireActual('fs');
+ mockReadFileSync.mockImplementation((path: string) => realFs.readFileSync(path));
+ const utils = jest.requireActual('../../../../../../src/core/utils');
+ mockReadPkcs12Keystore.mockImplementation((path: string, password?: string) =>
+ utils.readPkcs12Keystore(path, password)
+ );
+ mockReadPkcs12Truststore.mockImplementation((path: string, password?: string) =>
+ utils.readPkcs12Truststore(path, password)
+ );
+ });
+
+ it('throws if key is invalid', () => {
+ const value = { ssl: { key: '/invalid/key' } };
+ expect(() => parse(value)).toThrowErrorMatchingInlineSnapshot(
+ `"ENOENT: no such file or directory, open '/invalid/key'"`
+ );
+ });
+
+ it('throws if certificate is invalid', () => {
+ const value = { ssl: { certificate: '/invalid/cert' } };
+ expect(() => parse(value)).toThrowErrorMatchingInlineSnapshot(
+ `"ENOENT: no such file or directory, open '/invalid/cert'"`
+ );
+ });
+
+ it('throws if certificateAuthorities is invalid', () => {
+ const value = { ssl: { certificateAuthorities: '/invalid/ca' } };
+ expect(() => parse(value)).toThrowErrorMatchingInlineSnapshot(
+ `"ENOENT: no such file or directory, open '/invalid/ca'"`
+ );
+ });
+
+ it('throws if keystore path is invalid', () => {
+ const value = { ssl: { keystore: { path: '/invalid/keystore' } } };
+ expect(() => parse(value)).toThrowErrorMatchingInlineSnapshot(
+ `"ENOENT: no such file or directory, open '/invalid/keystore'"`
+ );
+ });
+
+ it('throws if keystore does not contain a key', () => {
+ mockReadPkcs12Keystore.mockReturnValueOnce({});
+ const value = { ssl: { keystore: { path: 'some-path' } } };
+ expect(() => parse(value)).toThrowErrorMatchingInlineSnapshot(
+ `"Did not find key in Elasticsearch keystore."`
+ );
+ });
+
+ it('throws if keystore does not contain a certificate', () => {
+ mockReadPkcs12Keystore.mockReturnValueOnce({ key: 'foo' });
+ const value = { ssl: { keystore: { path: 'some-path' } } };
+ expect(() => parse(value)).toThrowErrorMatchingInlineSnapshot(
+ `"Did not find certificate in Elasticsearch keystore."`
+ );
+ });
+
+ it('throws if truststore path is invalid', () => {
+ const value = { ssl: { keystore: { path: '/invalid/truststore' } } };
+ expect(() => parse(value)).toThrowErrorMatchingInlineSnapshot(
+ `"ENOENT: no such file or directory, open '/invalid/truststore'"`
+ );
+ });
+
+ it('throws if key and keystore.path are both specified', () => {
+ const value = { ssl: { key: 'foo', keystore: { path: 'bar' } } };
+ expect(() => parse(value)).toThrowErrorMatchingInlineSnapshot(
+ `"[config validation of [xpack.monitoring.elasticsearch].ssl]: cannot use [key] when [keystore.path] is specified"`
+ );
+ });
+
+ it('throws if certificate and keystore.path are both specified', () => {
+ const value = { ssl: { certificate: 'foo', keystore: { path: 'bar' } } };
+ expect(() => parse(value)).toThrowErrorMatchingInlineSnapshot(
+ `"[config validation of [xpack.monitoring.elasticsearch].ssl]: cannot use [certificate] when [keystore.path] is specified"`
+ );
+ });
+});
diff --git a/x-pack/legacy/plugins/monitoring/server/es_client/parse_elasticsearch_config.ts b/x-pack/legacy/plugins/monitoring/server/es_client/parse_elasticsearch_config.ts
new file mode 100644
index 0000000000000..70e6235602b5b
--- /dev/null
+++ b/x-pack/legacy/plugins/monitoring/server/es_client/parse_elasticsearch_config.ts
@@ -0,0 +1,111 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { readFileSync } from 'fs';
+import { readPkcs12Truststore, readPkcs12Keystore } from '../../../../../../src/core/utils';
+
+const KEY = 'xpack.monitoring.elasticsearch';
+
+/*
+ * Parse a config object's Elasticsearch configuration, reading any
+ * certificates/keys from the filesystem
+ *
+ * TODO: this code can be removed when this plugin is migrated to the Kibana Platform,
+ * at that point the ElasticsearchClient and ElasticsearchConfig should be used instead
+ */
+export const parseElasticsearchConfig = (config: any) => {
+ const es = config.get(KEY);
+
+ const errorPrefix = `[config validation of [${KEY}].ssl]`;
+ if (es.ssl?.key && es.ssl?.keystore?.path) {
+ throw new Error(`${errorPrefix}: cannot use [key] when [keystore.path] is specified`);
+ }
+ if (es.ssl?.certificate && es.ssl?.keystore?.path) {
+ throw new Error(`${errorPrefix}: cannot use [certificate] when [keystore.path] is specified`);
+ }
+
+ const { alwaysPresentCertificate, verificationMode } = es.ssl;
+ const { key, keyPassphrase, certificate, certificateAuthorities } = readKeyAndCerts(es);
+
+ return {
+ ...es,
+ ssl: {
+ alwaysPresentCertificate,
+ key,
+ keyPassphrase,
+ certificate,
+ certificateAuthorities,
+ verificationMode,
+ },
+ };
+};
+
+const readKeyAndCerts = (rawConfig: any) => {
+ let key: string | undefined;
+ let keyPassphrase: string | undefined;
+ let certificate: string | undefined;
+ let certificateAuthorities: string[] | undefined;
+
+ const addCAs = (ca: string[] | undefined) => {
+ if (ca && ca.length) {
+ certificateAuthorities = [...(certificateAuthorities || []), ...ca];
+ }
+ };
+
+ if (rawConfig.ssl.keystore?.path) {
+ const keystore = readPkcs12Keystore(
+ rawConfig.ssl.keystore.path,
+ rawConfig.ssl.keystore.password
+ );
+ if (!keystore.key) {
+ throw new Error(`Did not find key in Elasticsearch keystore.`);
+ } else if (!keystore.cert) {
+ throw new Error(`Did not find certificate in Elasticsearch keystore.`);
+ }
+ key = keystore.key;
+ certificate = keystore.cert;
+ addCAs(keystore.ca);
+ } else {
+ if (rawConfig.ssl.key) {
+ key = readFile(rawConfig.ssl.key);
+ keyPassphrase = rawConfig.ssl.keyPassphrase;
+ }
+ if (rawConfig.ssl.certificate) {
+ certificate = readFile(rawConfig.ssl.certificate);
+ }
+ }
+
+ if (rawConfig.ssl.truststore?.path) {
+ const ca = readPkcs12Truststore(
+ rawConfig.ssl.truststore.path,
+ rawConfig.ssl.truststore.password
+ );
+ addCAs(ca);
+ }
+
+ const ca = rawConfig.ssl.certificateAuthorities;
+ if (ca) {
+ const parsed: string[] = [];
+ const paths = Array.isArray(ca) ? ca : [ca];
+ if (paths.length > 0) {
+ for (const path of paths) {
+ parsed.push(readFile(path));
+ }
+ addCAs(parsed);
+ }
+ }
+
+ return {
+ key,
+ keyPassphrase,
+ certificate,
+ certificateAuthorities,
+ };
+};
+
+const readFile = (file: string) => {
+ return readFileSync(file, 'utf8');
+};
diff --git a/x-pack/legacy/plugins/monitoring/server/plugin.js b/x-pack/legacy/plugins/monitoring/server/plugin.js
index e26dd96dde1bf..163bc43945be1 100644
--- a/x-pack/legacy/plugins/monitoring/server/plugin.js
+++ b/x-pack/legacy/plugins/monitoring/server/plugin.js
@@ -11,6 +11,7 @@ import { instantiateClient } from './es_client/instantiate_client';
import { initMonitoringXpackInfo } from './init_monitoring_xpack_info';
import { initBulkUploader, registerCollectors } from './kibana_monitoring';
import { registerMonitoringCollection } from './telemetry_collection';
+import { parseElasticsearchConfig } from './es_client/parse_elasticsearch_config';
export class Plugin {
setup(core, plugins) {
@@ -36,6 +37,12 @@ export class Plugin {
* fetch methods and uploads to the ES monitoring bulk endpoint
*/
const xpackMainPlugin = plugins.xpack_main;
+
+ /*
+ * Parse the Elasticsearch config and read any certificates/keys if necessary
+ */
+ const elasticsearchConfig = parseElasticsearchConfig(config);
+
xpackMainPlugin.status.once('green', async () => {
// first time xpack_main turns green
/*
@@ -47,7 +54,7 @@ export class Plugin {
await instantiateClient({
log: core.log,
events: core.events,
- config,
+ elasticsearchConfig,
elasticsearchPlugin: plugins.elasticsearch,
}); // Instantiate the dedicated ES client
await initMonitoringXpackInfo({
From 01fe8afb987fc80125e84d965c5534bdcdc6e409 Mon Sep 17 00:00:00 2001
From: James Gowdy
Date: Tue, 21 Jan 2020 17:35:47 +0000
Subject: [PATCH 017/187] [ML] Fixing categorization wizard example results
(#54924)
* [ML] Fixing categorization wizard example results
* moving validation results to class
* cleaning up category analyzer types
* small tweaks
* removing commented out code
* fixing string ids
* small refactor
* improving validation messages
* fixing types
* updating message text
* fixing typo
* adding privileges error
* updating privilege message
* changes based on review
* removing old warning message
* fixing translations
* renaming enum
---
.../plugins/ml/common/constants/new_job.ts | 7 +
.../plugins/ml/common/types/categories.ts | 29 ++
.../ml/common/util/string_utils.test.ts | 24 +-
.../plugins/ml/common/util/string_utils.ts | 5 +
.../job_creator/categorization_job_creator.ts | 53 +--
.../common/job_validator/job_validator.ts | 4 +-
.../categorization_examples_loader.ts | 25 +-
.../new_job/common/results_loader/index.ts | 2 +-
.../examples_valid_callout.tsx | 64 ++--
.../categorization_view/field_examples.tsx | 4 +-
.../categorization_view/metric_selection.tsx | 34 +-
.../services/ml_api_service/index.d.ts | 15 +-
.../application/services/ml_server_info.ts | 8 +-
.../ml/server/models/job_service/index.js | 7 +-
.../job_service/new_job/categorization.ts | 314 ------------------
.../new_job/categorization/examples.ts | 206 ++++++++++++
.../new_job/categorization/index.ts | 8 +
.../new_job/categorization/top_categories.ts | 164 +++++++++
.../categorization/validation_results.ts | 208 ++++++++++++
.../models/job_service/new_job/index.ts | 2 +-
20 files changed, 763 insertions(+), 420 deletions(-)
delete mode 100644 x-pack/legacy/plugins/ml/server/models/job_service/new_job/categorization.ts
create mode 100644 x-pack/legacy/plugins/ml/server/models/job_service/new_job/categorization/examples.ts
create mode 100644 x-pack/legacy/plugins/ml/server/models/job_service/new_job/categorization/index.ts
create mode 100644 x-pack/legacy/plugins/ml/server/models/job_service/new_job/categorization/top_categories.ts
create mode 100644 x-pack/legacy/plugins/ml/server/models/job_service/new_job/categorization/validation_results.ts
diff --git a/x-pack/legacy/plugins/ml/common/constants/new_job.ts b/x-pack/legacy/plugins/ml/common/constants/new_job.ts
index 3c98b372afdf7..862fa72d11fdb 100644
--- a/x-pack/legacy/plugins/ml/common/constants/new_job.ts
+++ b/x-pack/legacy/plugins/ml/common/constants/new_job.ts
@@ -26,7 +26,14 @@ export const DEFAULT_QUERY_DELAY = '60s';
export const SHARED_RESULTS_INDEX_NAME = 'shared';
+// Categorization
export const NUMBER_OF_CATEGORY_EXAMPLES = 5;
export const CATEGORY_EXAMPLES_SAMPLE_SIZE = 1000;
export const CATEGORY_EXAMPLES_WARNING_LIMIT = 0.75;
export const CATEGORY_EXAMPLES_ERROR_LIMIT = 0.02;
+
+export enum CATEGORY_EXAMPLES_VALIDATION_STATUS {
+ VALID = 'valid',
+ PARTIALLY_VALID = 'partially_valid',
+ INVALID = 'invalid',
+}
diff --git a/x-pack/legacy/plugins/ml/common/types/categories.ts b/x-pack/legacy/plugins/ml/common/types/categories.ts
index 6ccd13ed9a39e..765053ced5201 100644
--- a/x-pack/legacy/plugins/ml/common/types/categories.ts
+++ b/x-pack/legacy/plugins/ml/common/types/categories.ts
@@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../constants/new_job';
+
export type CategoryId = number;
export interface Category {
@@ -23,3 +25,30 @@ export interface Token {
type: string;
position: number;
}
+
+export interface CategorizationAnalyzer {
+ char_filter?: any[];
+ tokenizer?: string;
+ filter?: any[];
+ analyzer?: string;
+}
+
+export interface CategoryFieldExample {
+ text: string;
+ tokens: Token[];
+}
+
+export enum VALIDATION_RESULT {
+ TOKEN_COUNT,
+ MEDIAN_LINE_LENGTH,
+ NULL_VALUES,
+ TOO_MANY_TOKENS,
+ FAILED_TO_TOKENIZE,
+ INSUFFICIENT_PRIVILEGES,
+}
+
+export interface FieldExampleCheck {
+ id: VALIDATION_RESULT;
+ valid: CATEGORY_EXAMPLES_VALIDATION_STATUS;
+ message: string;
+}
diff --git a/x-pack/legacy/plugins/ml/common/util/string_utils.test.ts b/x-pack/legacy/plugins/ml/common/util/string_utils.test.ts
index aba2dbd230ada..026c8e6110c99 100644
--- a/x-pack/legacy/plugins/ml/common/util/string_utils.test.ts
+++ b/x-pack/legacy/plugins/ml/common/util/string_utils.test.ts
@@ -4,7 +4,17 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { renderTemplate } from './string_utils';
+import { renderTemplate, getMedianStringLength } from './string_utils';
+
+const strings: string[] = [
+ 'foo',
+ 'foofoofoofoofoo',
+ 'foofoofoo',
+ 'f',
+ 'f',
+ 'foofoofoofoofoofoofoo',
+];
+const noStrings: string[] = [];
describe('ML - string utils', () => {
describe('renderTemplate', () => {
@@ -24,4 +34,16 @@ describe('ML - string utils', () => {
expect(result).toBe('string with 1 replacement, and a 2nd one.');
});
});
+
+ describe('getMedianStringLength', () => {
+ test('test median for string array', () => {
+ const result = getMedianStringLength(strings);
+ expect(result).toBe(9);
+ });
+
+ test('test median for no strings', () => {
+ const result = getMedianStringLength(noStrings);
+ expect(result).toBe(0);
+ });
+ });
});
diff --git a/x-pack/legacy/plugins/ml/common/util/string_utils.ts b/x-pack/legacy/plugins/ml/common/util/string_utils.ts
index 432baabe773cc..9dd2ce3d74cd5 100644
--- a/x-pack/legacy/plugins/ml/common/util/string_utils.ts
+++ b/x-pack/legacy/plugins/ml/common/util/string_utils.ts
@@ -17,3 +17,8 @@ export function renderTemplate(str: string, data?: Record): stri
return str;
}
+
+export function getMedianStringLength(strings: string[]) {
+ const sortedStringLengths = strings.map(s => s.length).sort((a, b) => a - b);
+ return sortedStringLengths[Math.floor(sortedStringLengths.length / 2)] || 0;
+}
diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts
index 71619311c4361..0ff0ffb6f3bb3 100644
--- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts
+++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts
@@ -16,25 +16,31 @@ import {
CREATED_BY_LABEL,
DEFAULT_BUCKET_SPAN,
DEFAULT_RARE_BUCKET_SPAN,
+ CATEGORY_EXAMPLES_VALIDATION_STATUS,
} from '../../../../../../common/constants/new_job';
import { ML_JOB_AGGREGATION } from '../../../../../../common/constants/aggregation_types';
+import {
+ CategorizationAnalyzer,
+ CategoryFieldExample,
+ FieldExampleCheck,
+} from '../../../../../../common/types/categories';
import { getRichDetectors } from './util/general';
-import { CategorizationExamplesLoader, CategoryExample } from '../results_loader';
-import { CategorizationAnalyzer, getNewJobDefaults } from '../../../../services/ml_server_info';
-
-type CategorizationAnalyzerType = CategorizationAnalyzer | null;
+import { CategorizationExamplesLoader } from '../results_loader';
+import { getNewJobDefaults } from '../../../../services/ml_server_info';
export class CategorizationJobCreator extends JobCreator {
protected _type: JOB_TYPE = JOB_TYPE.CATEGORIZATION;
private _createCountDetector: () => void = () => {};
private _createRareDetector: () => void = () => {};
private _examplesLoader: CategorizationExamplesLoader;
- private _categoryFieldExamples: CategoryExample[] = [];
- private _categoryFieldValid: number = 0;
+ private _categoryFieldExamples: CategoryFieldExample[] = [];
+ private _validationChecks: FieldExampleCheck[] = [];
+ private _overallValidStatus: CATEGORY_EXAMPLES_VALIDATION_STATUS =
+ CATEGORY_EXAMPLES_VALIDATION_STATUS.INVALID;
private _detectorType: ML_JOB_AGGREGATION.COUNT | ML_JOB_AGGREGATION.RARE =
ML_JOB_AGGREGATION.COUNT;
- private _categorizationAnalyzer: CategorizationAnalyzerType = null;
- private _defaultCategorizationAnalyzer: CategorizationAnalyzerType;
+ private _categorizationAnalyzer: CategorizationAnalyzer = {};
+ private _defaultCategorizationAnalyzer: CategorizationAnalyzer;
constructor(
indexPattern: IndexPattern,
@@ -46,7 +52,7 @@ export class CategorizationJobCreator extends JobCreator {
this._examplesLoader = new CategorizationExamplesLoader(this, indexPattern, query);
const { anomaly_detectors: anomalyDetectors } = getNewJobDefaults();
- this._defaultCategorizationAnalyzer = anomalyDetectors.categorization_analyzer || null;
+ this._defaultCategorizationAnalyzer = anomalyDetectors.categorization_analyzer || {};
}
public setDefaultDetectorProperties(
@@ -93,7 +99,7 @@ export class CategorizationJobCreator extends JobCreator {
} else {
delete this._job_config.analysis_config.categorization_field_name;
this._categoryFieldExamples = [];
- this._categoryFieldValid = 0;
+ this._validationChecks = [];
}
}
@@ -102,31 +108,38 @@ export class CategorizationJobCreator extends JobCreator {
}
public async loadCategorizationFieldExamples() {
- const { valid, examples, sampleSize } = await this._examplesLoader.loadExamples();
+ const {
+ examples,
+ sampleSize,
+ overallValidStatus,
+ validationChecks,
+ } = await this._examplesLoader.loadExamples();
this._categoryFieldExamples = examples;
- this._categoryFieldValid = valid;
- return { valid, examples, sampleSize };
+ this._validationChecks = validationChecks;
+ this._overallValidStatus = overallValidStatus;
+ return { examples, sampleSize, overallValidStatus, validationChecks };
}
public get categoryFieldExamples() {
return this._categoryFieldExamples;
}
- public get categoryFieldValid() {
- return this._categoryFieldValid;
+ public get validationChecks() {
+ return this._validationChecks;
+ }
+
+ public get overallValidStatus() {
+ return this._overallValidStatus;
}
public get selectedDetectorType() {
return this._detectorType;
}
- public set categorizationAnalyzer(analyzer: CategorizationAnalyzerType) {
+ public set categorizationAnalyzer(analyzer: CategorizationAnalyzer) {
this._categorizationAnalyzer = analyzer;
- if (
- analyzer === null ||
- isEqual(this._categorizationAnalyzer, this._defaultCategorizationAnalyzer)
- ) {
+ if (isEqual(this._categorizationAnalyzer, this._defaultCategorizationAnalyzer)) {
delete this._job_config.analysis_config.categorization_analyzer;
} else {
this._job_config.analysis_config.categorization_analyzer = analyzer;
diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/job_validator/job_validator.ts b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/job_validator/job_validator.ts
index 976e94b377ae8..8f6b16c407fb6 100644
--- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/job_validator/job_validator.ts
+++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/job_validator/job_validator.ts
@@ -16,7 +16,7 @@ import { JobCreator, JobCreatorType, isCategorizationJobCreator } from '../job_c
import { populateValidationMessages, checkForExistingJobAndGroupIds } from './util';
import { ExistingJobsAndGroups } from '../../../../services/job_service';
import { cardinalityValidator, CardinalityValidatorResult } from './validators';
-import { CATEGORY_EXAMPLES_ERROR_LIMIT } from '../../../../../../common/constants/new_job';
+import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../../../../../../common/constants/new_job';
// delay start of validation to allow the user to make changes
// e.g. if they are typing in a new value, try not to validate
@@ -207,7 +207,7 @@ export class JobValidator {
private _runAdvancedValidation() {
if (isCategorizationJobCreator(this._jobCreator)) {
this._advancedValidations.categorizationFieldValid.valid =
- this._jobCreator.categoryFieldValid > CATEGORY_EXAMPLES_ERROR_LIMIT;
+ this._jobCreator.overallValidStatus !== CATEGORY_EXAMPLES_VALIDATION_STATUS.INVALID;
}
}
diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/results_loader/categorization_examples_loader.ts b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/results_loader/categorization_examples_loader.ts
index ce1ea0bdaf181..62a4d070fec32 100644
--- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/results_loader/categorization_examples_loader.ts
+++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/results_loader/categorization_examples_loader.ts
@@ -6,15 +6,12 @@
import { IndexPattern } from '../../../../../../../../../../src/plugins/data/public';
import { IndexPatternTitle } from '../../../../../../common/types/kibana';
-import { Token } from '../../../../../../common/types/categories';
import { CategorizationJobCreator } from '../job_creator';
import { ml } from '../../../../services/ml_api_service';
-import { NUMBER_OF_CATEGORY_EXAMPLES } from '../../../../../../common/constants/new_job';
-
-export interface CategoryExample {
- text: string;
- tokens: Token[];
-}
+import {
+ NUMBER_OF_CATEGORY_EXAMPLES,
+ CATEGORY_EXAMPLES_VALIDATION_STATUS,
+} from '../../../../../../common/constants/new_job';
export class CategorizationExamplesLoader {
private _jobCreator: CategorizationJobCreator;
@@ -36,20 +33,22 @@ export class CategorizationExamplesLoader {
const analyzer = this._jobCreator.categorizationAnalyzer;
const categorizationFieldName = this._jobCreator.categorizationFieldName;
if (categorizationFieldName === null) {
- return { valid: 0, examples: [], sampleSize: 0 };
+ return {
+ examples: [],
+ sampleSize: 0,
+ overallValidStatus: CATEGORY_EXAMPLES_VALIDATION_STATUS.INVALID,
+ validationChecks: [],
+ };
}
- const start = Math.floor(
- this._jobCreator.start + (this._jobCreator.end - this._jobCreator.start) / 2
- );
const resp = await ml.jobs.categorizationFieldExamples(
this._indexPatternTitle,
this._query,
NUMBER_OF_CATEGORY_EXAMPLES,
categorizationFieldName,
this._timeFieldName,
- start,
- 0,
+ this._jobCreator.start,
+ this._jobCreator.end,
analyzer
);
return resp;
diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/results_loader/index.ts b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/results_loader/index.ts
index 724c62f22e469..e15d859f8e6c3 100644
--- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/results_loader/index.ts
+++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/results_loader/index.ts
@@ -5,4 +5,4 @@
*/
export { ResultsLoader, Results, ModelItem, Anomaly } from './results_loader';
-export { CategorizationExamplesLoader, CategoryExample } from './categorization_examples_loader';
+export { CategorizationExamplesLoader } from './categorization_examples_loader';
diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/examples_valid_callout.tsx b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/examples_valid_callout.tsx
index 270ba99d938cd..ac886a3aea61a 100644
--- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/examples_valid_callout.tsx
+++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/examples_valid_callout.tsx
@@ -9,27 +9,24 @@ import { EuiCallOut, EuiSpacer, EuiCallOutProps } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
-import { CategorizationAnalyzer } from '../../../../../../../services/ml_server_info';
-import { EditCategorizationAnalyzerFlyout } from '../../../common/edit_categorization_analyzer_flyout';
import {
- CATEGORY_EXAMPLES_ERROR_LIMIT,
- CATEGORY_EXAMPLES_WARNING_LIMIT,
-} from '../../../../../../../../../common/constants/new_job';
-
-type CategorizationAnalyzerType = CategorizationAnalyzer | null;
+ CategorizationAnalyzer,
+ FieldExampleCheck,
+} from '../../../../../../../../../common/types/categories';
+import { EditCategorizationAnalyzerFlyout } from '../../../common/edit_categorization_analyzer_flyout';
+import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../../../../../../../../../common/constants/new_job';
interface Props {
- examplesValid: number;
- sampleSize: number;
- categorizationAnalyzer: CategorizationAnalyzerType;
+ validationChecks: FieldExampleCheck[];
+ overallValidStatus: CATEGORY_EXAMPLES_VALIDATION_STATUS;
+ categorizationAnalyzer: CategorizationAnalyzer;
}
export const ExamplesValidCallout: FC = ({
- examplesValid,
+ overallValidStatus,
+ validationChecks,
categorizationAnalyzer,
- sampleSize,
}) => {
- const percentageText = ;
const analyzerUsed = ;
let color: EuiCallOutProps['color'] = 'success';
@@ -40,7 +37,7 @@ export const ExamplesValidCallout: FC = ({
}
);
- if (examplesValid < CATEGORY_EXAMPLES_ERROR_LIMIT) {
+ if (overallValidStatus === CATEGORY_EXAMPLES_VALIDATION_STATUS.INVALID) {
color = 'danger';
title = i18n.translate(
'xpack.ml.newJob.wizard.pickFieldsStep.categorizationFieldCalloutTitle.invalid',
@@ -48,7 +45,7 @@ export const ExamplesValidCallout: FC