mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Clean angular from moved code, global state and legacy shims (#115420)
This commit is contained in:
parent
85d7115d4a
commit
eb5ffff7d0
13 changed files with 18 additions and 783 deletions
|
@ -50,17 +50,13 @@ const initLegacyShims = () => {
|
|||
ruleTypeRegistry: ruleTypeRegistryMock.create(),
|
||||
};
|
||||
const data = { query: { timefilter: { timefilter: {} } } } as any;
|
||||
const ngInjector = {} as angular.auto.IInjectorService;
|
||||
Legacy.init(
|
||||
{
|
||||
core: coreMock.createStart(),
|
||||
data,
|
||||
isCloud: false,
|
||||
triggersActionsUi,
|
||||
usageCollection: {},
|
||||
} as any,
|
||||
ngInjector
|
||||
);
|
||||
Legacy.init({
|
||||
core: coreMock.createStart(),
|
||||
data,
|
||||
isCloud: false,
|
||||
triggersActionsUi,
|
||||
usageCollection: {},
|
||||
} as any);
|
||||
};
|
||||
|
||||
const ALERTS_FEATURE_ID = 'alerts';
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IDirective, IRootElementService, IScope } from 'angular';
|
||||
|
||||
import { I18nServiceType } from './provider';
|
||||
|
||||
interface I18nScope extends IScope {
|
||||
values?: Record<string, any>;
|
||||
defaultMessage: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
const HTML_KEY_PREFIX = 'html_';
|
||||
const PLACEHOLDER_SEPARATOR = '@I18N@';
|
||||
|
||||
export const i18nDirective: [string, string, typeof i18nDirectiveFn] = [
|
||||
'i18n',
|
||||
'$sanitize',
|
||||
i18nDirectiveFn,
|
||||
];
|
||||
|
||||
function i18nDirectiveFn(
|
||||
i18n: I18nServiceType,
|
||||
$sanitize: (html: string) => string
|
||||
): IDirective<I18nScope> {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
id: '@i18nId',
|
||||
defaultMessage: '@i18nDefaultMessage',
|
||||
values: '<?i18nValues',
|
||||
},
|
||||
link($scope, $element) {
|
||||
if ($scope.values) {
|
||||
$scope.$watchCollection('values', () => {
|
||||
setContent($element, $scope, $sanitize, i18n);
|
||||
});
|
||||
} else {
|
||||
setContent($element, $scope, $sanitize, i18n);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function setContent(
|
||||
$element: IRootElementService,
|
||||
$scope: I18nScope,
|
||||
$sanitize: (html: string) => string,
|
||||
i18n: I18nServiceType
|
||||
) {
|
||||
const originalValues = $scope.values;
|
||||
const valuesWithPlaceholders = {} as Record<string, any>;
|
||||
let hasValuesWithPlaceholders = false;
|
||||
|
||||
// If we have values with the keys that start with HTML_KEY_PREFIX we should replace
|
||||
// them with special placeholders that later on will be inserted as HTML
|
||||
// into the DOM, the rest of the content will be treated as text. We don't
|
||||
// sanitize values at this stage as some of the values can be excluded from
|
||||
// the translated string (e.g. not used by ICU conditional statements).
|
||||
if (originalValues) {
|
||||
for (const [key, value] of Object.entries(originalValues)) {
|
||||
if (key.startsWith(HTML_KEY_PREFIX)) {
|
||||
valuesWithPlaceholders[
|
||||
key.slice(HTML_KEY_PREFIX.length)
|
||||
] = `${PLACEHOLDER_SEPARATOR}${key}${PLACEHOLDER_SEPARATOR}`;
|
||||
|
||||
hasValuesWithPlaceholders = true;
|
||||
} else {
|
||||
valuesWithPlaceholders[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const label = i18n($scope.id, {
|
||||
values: valuesWithPlaceholders,
|
||||
defaultMessage: $scope.defaultMessage,
|
||||
});
|
||||
|
||||
// If there are no placeholders to replace treat everything as text, otherwise
|
||||
// insert label piece by piece replacing every placeholder with corresponding
|
||||
// sanitized HTML content.
|
||||
if (!hasValuesWithPlaceholders) {
|
||||
$element.text(label);
|
||||
} else {
|
||||
$element.empty();
|
||||
for (const contentOrPlaceholder of label.split(PLACEHOLDER_SEPARATOR)) {
|
||||
if (!contentOrPlaceholder) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$element.append(
|
||||
originalValues!.hasOwnProperty(contentOrPlaceholder)
|
||||
? $sanitize(originalValues![contentOrPlaceholder])
|
||||
: document.createTextNode(contentOrPlaceholder)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { I18nServiceType } from './provider';
|
||||
|
||||
export const i18nFilter: [string, typeof i18nFilterFn] = ['i18n', i18nFilterFn];
|
||||
|
||||
function i18nFilterFn(i18n: I18nServiceType) {
|
||||
return (id: string, { defaultMessage = '', values = {} } = {}) => {
|
||||
return i18n(id, {
|
||||
values,
|
||||
defaultMessage,
|
||||
});
|
||||
};
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export { I18nProvider } from './provider';
|
||||
|
||||
export { i18nFilter } from './filter';
|
||||
export { i18nDirective } from './directive';
|
||||
|
||||
// re-export types: https://github.com/babel/babel-loader/issues/603
|
||||
import { I18nServiceType as _I18nServiceType } from './provider';
|
||||
export type I18nServiceType = _I18nServiceType;
|
|
@ -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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export type I18nServiceType = ReturnType<I18nProvider['$get']>;
|
||||
|
||||
export class I18nProvider implements angular.IServiceProvider {
|
||||
public addTranslation = i18n.addTranslation;
|
||||
public getTranslation = i18n.getTranslation;
|
||||
public setLocale = i18n.setLocale;
|
||||
public getLocale = i18n.getLocale;
|
||||
public setDefaultLocale = i18n.setDefaultLocale;
|
||||
public getDefaultLocale = i18n.getDefaultLocale;
|
||||
public setFormats = i18n.setFormats;
|
||||
public getFormats = i18n.getFormats;
|
||||
public getRegisteredLocales = i18n.getRegisteredLocales;
|
||||
public init = i18n.init;
|
||||
public load = i18n.load;
|
||||
public $get = () => i18n.translate;
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { IHttpResponse } from 'angular';
|
||||
|
||||
type AngularHttpError = IHttpResponse<{ message: string }>;
|
||||
|
||||
export function isAngularHttpError(error: any): error is AngularHttpError {
|
||||
return (
|
||||
error &&
|
||||
typeof error.status === 'number' &&
|
||||
typeof error.statusText === 'string' &&
|
||||
error.data &&
|
||||
typeof error.data.message === 'string'
|
||||
);
|
||||
}
|
||||
|
||||
export function formatAngularHttpError(error: AngularHttpError) {
|
||||
// is an Angular $http "error object"
|
||||
if (error.status === -1) {
|
||||
// status = -1 indicates that the request was failed to reach the server
|
||||
return i18n.translate('xpack.monitoring.notify.fatalError.unavailableServerErrorMessage', {
|
||||
defaultMessage:
|
||||
'An HTTP request has failed to connect. ' +
|
||||
'Please check if the Kibana server is running and that your browser has a working connection, ' +
|
||||
'or contact your system administrator.',
|
||||
});
|
||||
}
|
||||
|
||||
return i18n.translate('xpack.monitoring.notify.fatalError.errorStatusMessage', {
|
||||
defaultMessage: 'Error {errStatus} {errStatusText}: {errMessage}',
|
||||
values: {
|
||||
errStatus: error.status,
|
||||
errStatusText: error.statusText,
|
||||
errMessage: error.data.message,
|
||||
},
|
||||
});
|
||||
}
|
|
@ -1,349 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
ICompileProvider,
|
||||
IHttpProvider,
|
||||
IHttpService,
|
||||
ILocationProvider,
|
||||
IModule,
|
||||
IRootScopeService,
|
||||
IRequestConfig,
|
||||
} from 'angular';
|
||||
import $ from 'jquery';
|
||||
import { set } from '@elastic/safer-lodash-set';
|
||||
import { get } from 'lodash';
|
||||
import * as Rx from 'rxjs';
|
||||
import { ChromeBreadcrumb, EnvironmentMode, PackageInfo } from 'kibana/public';
|
||||
import { History } from 'history';
|
||||
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { formatAngularHttpError, isAngularHttpError } from '../helpers/format_angular_http_error';
|
||||
|
||||
export interface RouteConfiguration {
|
||||
controller?: string | ((...args: any[]) => void);
|
||||
redirectTo?: string;
|
||||
resolveRedirectTo?: (...args: any[]) => void;
|
||||
reloadOnSearch?: boolean;
|
||||
reloadOnUrl?: boolean;
|
||||
outerAngularWrapperRoute?: boolean;
|
||||
resolve?: object;
|
||||
template?: string;
|
||||
k7Breadcrumbs?: (...args: any[]) => ChromeBreadcrumb[];
|
||||
requireUICapability?: string;
|
||||
}
|
||||
|
||||
function isSystemApiRequest(request: IRequestConfig) {
|
||||
const { headers } = request;
|
||||
return headers && !!headers['kbn-system-request'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects whether a given angular route is a dummy route that doesn't
|
||||
* require any action. There are two ways this can happen:
|
||||
* If `outerAngularWrapperRoute` is set on the route config object,
|
||||
* it means the local application service set up this route on the outer angular
|
||||
* and the internal routes will handle the hooks.
|
||||
*
|
||||
* If angular did not detect a route and it is the local angular, we are currently
|
||||
* navigating away from a URL controlled by a local angular router and the
|
||||
* application will get unmounted. In this case the outer router will handle
|
||||
* the hooks.
|
||||
* @param $route Injected $route dependency
|
||||
* @param isLocalAngular Flag whether this is the local angular router
|
||||
*/
|
||||
function isDummyRoute($route: any, isLocalAngular: boolean) {
|
||||
return (
|
||||
($route.current && $route.current.$$route && $route.current.$$route.outerAngularWrapperRoute) ||
|
||||
(!$route.current && isLocalAngular)
|
||||
);
|
||||
}
|
||||
|
||||
export const configureAppAngularModule = (
|
||||
angularModule: IModule,
|
||||
newPlatform: {
|
||||
core: CoreStart;
|
||||
readonly env: {
|
||||
mode: Readonly<EnvironmentMode>;
|
||||
packageInfo: Readonly<PackageInfo>;
|
||||
};
|
||||
},
|
||||
isLocalAngular: boolean,
|
||||
getHistory?: () => History
|
||||
) => {
|
||||
const core = 'core' in newPlatform ? newPlatform.core : newPlatform;
|
||||
const packageInfo = newPlatform.env.packageInfo;
|
||||
|
||||
angularModule
|
||||
.value('kbnVersion', packageInfo.version)
|
||||
.value('buildNum', packageInfo.buildNum)
|
||||
.value('buildSha', packageInfo.buildSha)
|
||||
.value('esUrl', getEsUrl(core))
|
||||
.value('uiCapabilities', core.application.capabilities)
|
||||
.config(setupCompileProvider(newPlatform.env.mode.dev))
|
||||
.config(setupLocationProvider())
|
||||
.config($setupXsrfRequestInterceptor(packageInfo.version))
|
||||
.run(capture$httpLoadingCount(core))
|
||||
.run(digestOnHashChange(getHistory))
|
||||
.run($setupBreadcrumbsAutoClear(core, isLocalAngular))
|
||||
.run($setupBadgeAutoClear(core, isLocalAngular))
|
||||
.run($setupHelpExtensionAutoClear(core, isLocalAngular))
|
||||
.run($setupUICapabilityRedirect(core));
|
||||
};
|
||||
|
||||
const getEsUrl = (newPlatform: CoreStart) => {
|
||||
const a = document.createElement('a');
|
||||
a.href = newPlatform.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,
|
||||
};
|
||||
};
|
||||
|
||||
const digestOnHashChange = (getHistory?: () => History) => ($rootScope: IRootScopeService) => {
|
||||
if (!getHistory) return;
|
||||
const unlisten = getHistory().listen(() => {
|
||||
// dispatch synthetic hash change event to update hash history objects and angular routing
|
||||
// this is necessary because hash updates triggered by using popState won't trigger this event naturally.
|
||||
// this has to happen in the next tick to not change the existing timing of angular digest cycles.
|
||||
setTimeout(() => {
|
||||
window.dispatchEvent(new HashChangeEvent('hashchange'));
|
||||
}, 0);
|
||||
});
|
||||
$rootScope.$on('$destroy', unlisten);
|
||||
};
|
||||
|
||||
const setupCompileProvider = (devMode: boolean) => ($compileProvider: ICompileProvider) => {
|
||||
if (!devMode) {
|
||||
$compileProvider.debugInfoEnabled(false);
|
||||
}
|
||||
};
|
||||
|
||||
const setupLocationProvider = () => ($locationProvider: ILocationProvider) => {
|
||||
$locationProvider.html5Mode({
|
||||
enabled: false,
|
||||
requireBase: false,
|
||||
rewriteLinks: false,
|
||||
});
|
||||
|
||||
$locationProvider.hashPrefix('');
|
||||
};
|
||||
|
||||
export const $setupXsrfRequestInterceptor = (version: string) => {
|
||||
// Configure jQuery prefilter
|
||||
$.ajaxPrefilter(({ kbnXsrfToken = true }: any, originalOptions, jqXHR) => {
|
||||
if (kbnXsrfToken) {
|
||||
jqXHR.setRequestHeader('kbn-version', version);
|
||||
}
|
||||
});
|
||||
|
||||
return ($httpProvider: IHttpProvider) => {
|
||||
// Configure $httpProvider interceptor
|
||||
$httpProvider.interceptors.push(() => {
|
||||
return {
|
||||
request(opts) {
|
||||
const { kbnXsrfToken = true } = opts as any;
|
||||
if (kbnXsrfToken) {
|
||||
set(opts, ['headers', 'kbn-version'], version);
|
||||
}
|
||||
return opts;
|
||||
},
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Injected into angular module by ui/chrome angular integration
|
||||
* and adds a root-level watcher that will capture the count of
|
||||
* active $http requests on each digest loop and expose the count to
|
||||
* the core.loadingCount api
|
||||
*/
|
||||
const capture$httpLoadingCount =
|
||||
(newPlatform: CoreStart) => ($rootScope: IRootScopeService, $http: IHttpService) => {
|
||||
newPlatform.http.addLoadingCountSource(
|
||||
new Rx.Observable((observer) => {
|
||||
const unwatch = $rootScope.$watch(() => {
|
||||
const reqs = $http.pendingRequests || [];
|
||||
observer.next(reqs.filter((req) => !isSystemApiRequest(req)).length);
|
||||
});
|
||||
|
||||
return unwatch;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* integrates with angular to automatically redirect to home if required
|
||||
* capability is not met
|
||||
*/
|
||||
const $setupUICapabilityRedirect =
|
||||
(newPlatform: CoreStart) => ($rootScope: IRootScopeService, $injector: any) => {
|
||||
const isKibanaAppRoute = window.location.pathname.endsWith('/app/kibana');
|
||||
// this feature only works within kibana app for now after everything is
|
||||
// switched to the application service, this can be changed to handle all
|
||||
// apps.
|
||||
if (!isKibanaAppRoute) {
|
||||
return;
|
||||
}
|
||||
$rootScope.$on(
|
||||
'$routeChangeStart',
|
||||
(event, { $$route: route }: { $$route?: RouteConfiguration } = {}) => {
|
||||
if (!route || !route.requireUICapability) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!get(newPlatform.application.capabilities, route.requireUICapability)) {
|
||||
$injector.get('$location').url('/home');
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* internal angular run function that will be called when angular bootstraps and
|
||||
* lets us integrate with the angular router so that we can automatically clear
|
||||
* the breadcrumbs if we switch to a Kibana app that does not use breadcrumbs correctly
|
||||
*/
|
||||
const $setupBreadcrumbsAutoClear =
|
||||
(newPlatform: CoreStart, isLocalAngular: boolean) =>
|
||||
($rootScope: IRootScopeService, $injector: any) => {
|
||||
// A flag used to determine if we should automatically
|
||||
// clear the breadcrumbs between angular route changes.
|
||||
let breadcrumbSetSinceRouteChange = false;
|
||||
const $route = $injector.has('$route') ? $injector.get('$route') : {};
|
||||
|
||||
// reset breadcrumbSetSinceRouteChange any time the breadcrumbs change, even
|
||||
// if it was done directly through the new platform
|
||||
newPlatform.chrome.getBreadcrumbs$().subscribe({
|
||||
next() {
|
||||
breadcrumbSetSinceRouteChange = true;
|
||||
},
|
||||
});
|
||||
|
||||
$rootScope.$on('$routeChangeStart', () => {
|
||||
breadcrumbSetSinceRouteChange = false;
|
||||
});
|
||||
|
||||
$rootScope.$on('$routeChangeSuccess', () => {
|
||||
if (isDummyRoute($route, isLocalAngular)) {
|
||||
return;
|
||||
}
|
||||
const current = $route.current || {};
|
||||
|
||||
if (breadcrumbSetSinceRouteChange || (current.$$route && current.$$route.redirectTo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const k7BreadcrumbsProvider = current.k7Breadcrumbs;
|
||||
if (!k7BreadcrumbsProvider) {
|
||||
newPlatform.chrome.setBreadcrumbs([]);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
newPlatform.chrome.setBreadcrumbs($injector.invoke(k7BreadcrumbsProvider));
|
||||
} catch (error) {
|
||||
if (isAngularHttpError(error)) {
|
||||
error = formatAngularHttpError(error);
|
||||
}
|
||||
newPlatform.fatalErrors.add(error, 'location');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* internal angular run function that will be called when angular bootstraps and
|
||||
* lets us integrate with the angular router so that we can automatically clear
|
||||
* the badge if we switch to a Kibana app that does not use the badge correctly
|
||||
*/
|
||||
const $setupBadgeAutoClear =
|
||||
(newPlatform: CoreStart, isLocalAngular: boolean) =>
|
||||
($rootScope: IRootScopeService, $injector: any) => {
|
||||
// A flag used to determine if we should automatically
|
||||
// clear the badge between angular route changes.
|
||||
let badgeSetSinceRouteChange = false;
|
||||
const $route = $injector.has('$route') ? $injector.get('$route') : {};
|
||||
|
||||
$rootScope.$on('$routeChangeStart', () => {
|
||||
badgeSetSinceRouteChange = false;
|
||||
});
|
||||
|
||||
$rootScope.$on('$routeChangeSuccess', () => {
|
||||
if (isDummyRoute($route, isLocalAngular)) {
|
||||
return;
|
||||
}
|
||||
const current = $route.current || {};
|
||||
|
||||
if (badgeSetSinceRouteChange || (current.$$route && current.$$route.redirectTo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const badgeProvider = current.badge;
|
||||
if (!badgeProvider) {
|
||||
newPlatform.chrome.setBadge(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
newPlatform.chrome.setBadge($injector.invoke(badgeProvider));
|
||||
} catch (error) {
|
||||
if (isAngularHttpError(error)) {
|
||||
error = formatAngularHttpError(error);
|
||||
}
|
||||
newPlatform.fatalErrors.add(error, 'location');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* internal angular run function that will be called when angular bootstraps and
|
||||
* lets us integrate with the angular router so that we can automatically clear
|
||||
* the helpExtension if we switch to a Kibana app that does not set its own
|
||||
* helpExtension
|
||||
*/
|
||||
const $setupHelpExtensionAutoClear =
|
||||
(newPlatform: CoreStart, isLocalAngular: boolean) =>
|
||||
($rootScope: IRootScopeService, $injector: any) => {
|
||||
/**
|
||||
* reset helpExtensionSetSinceRouteChange any time the helpExtension changes, even
|
||||
* if it was done directly through the new platform
|
||||
*/
|
||||
let helpExtensionSetSinceRouteChange = false;
|
||||
newPlatform.chrome.getHelpExtension$().subscribe({
|
||||
next() {
|
||||
helpExtensionSetSinceRouteChange = true;
|
||||
},
|
||||
});
|
||||
|
||||
const $route = $injector.has('$route') ? $injector.get('$route') : {};
|
||||
|
||||
$rootScope.$on('$routeChangeStart', () => {
|
||||
if (isDummyRoute($route, isLocalAngular)) {
|
||||
return;
|
||||
}
|
||||
helpExtensionSetSinceRouteChange = false;
|
||||
});
|
||||
|
||||
$rootScope.$on('$routeChangeSuccess', () => {
|
||||
if (isDummyRoute($route, isLocalAngular)) {
|
||||
return;
|
||||
}
|
||||
const current = $route.current || {};
|
||||
|
||||
if (helpExtensionSetSinceRouteChange || (current.$$route && current.$$route.redirectTo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
newPlatform.chrome.setHelpExtension(current.helpExtension);
|
||||
});
|
||||
};
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './angular_config';
|
||||
// @ts-ignore
|
||||
export { createTopNavDirective, createTopNavHelper, loadKbnTopNavDirectives } from './kbn_top_nav';
|
|
@ -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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Injectable, IDirectiveFactory, IScope, IAttributes, IController } from 'angular';
|
||||
|
||||
export const createTopNavDirective: Injectable<
|
||||
IDirectiveFactory<IScope, JQLite, IAttributes, IController>
|
||||
>;
|
||||
export const createTopNavHelper: (
|
||||
options: unknown
|
||||
) => Injectable<IDirectiveFactory<IScope, JQLite, IAttributes, IController>>;
|
||||
export function loadKbnTopNavDirectives(navUi: unknown): void;
|
|
@ -1,119 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import angular from 'angular';
|
||||
import 'ngreact';
|
||||
|
||||
export function createTopNavDirective() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: '',
|
||||
compile: (elem) => {
|
||||
const child = document.createElement('kbn-top-nav-helper');
|
||||
|
||||
// Copy attributes to the child directive
|
||||
for (const attr of elem[0].attributes) {
|
||||
child.setAttribute(attr.name, attr.value);
|
||||
}
|
||||
|
||||
// Add a special attribute that will change every time that one
|
||||
// of the config array's disableButton function return value changes.
|
||||
child.setAttribute('disabled-buttons', 'disabledButtons');
|
||||
|
||||
// Append helper directive
|
||||
elem.append(child);
|
||||
|
||||
const linkFn = ($scope, _, $attr) => {
|
||||
// Watch config changes
|
||||
$scope.$watch(
|
||||
() => {
|
||||
const config = $scope.$eval($attr.config) || [];
|
||||
return config.map((item) => {
|
||||
// Copy key into id, as it's a reserved react propery.
|
||||
// This is done for Angular directive backward compatibility.
|
||||
// In React only id is recognized.
|
||||
if (item.key && !item.id) {
|
||||
item.id = item.key;
|
||||
}
|
||||
|
||||
// Watch the disableButton functions
|
||||
if (typeof item.disableButton === 'function') {
|
||||
return item.disableButton();
|
||||
}
|
||||
return item.disableButton;
|
||||
});
|
||||
},
|
||||
(newVal) => {
|
||||
$scope.disabledButtons = newVal;
|
||||
},
|
||||
true
|
||||
);
|
||||
};
|
||||
|
||||
return linkFn;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const createTopNavHelper =
|
||||
({ TopNavMenu }) =>
|
||||
(reactDirective) => {
|
||||
return reactDirective(TopNavMenu, [
|
||||
['config', { watchDepth: 'value' }],
|
||||
['setMenuMountPoint', { watchDepth: 'reference' }],
|
||||
['disabledButtons', { watchDepth: 'reference' }],
|
||||
|
||||
['query', { watchDepth: 'reference' }],
|
||||
['savedQuery', { watchDepth: 'reference' }],
|
||||
['intl', { watchDepth: 'reference' }],
|
||||
|
||||
['onQuerySubmit', { watchDepth: 'reference' }],
|
||||
['onFiltersUpdated', { watchDepth: 'reference' }],
|
||||
['onRefreshChange', { watchDepth: 'reference' }],
|
||||
['onClearSavedQuery', { watchDepth: 'reference' }],
|
||||
['onSaved', { watchDepth: 'reference' }],
|
||||
['onSavedQueryUpdated', { watchDepth: 'reference' }],
|
||||
['onSavedQueryIdChange', { watchDepth: 'reference' }],
|
||||
|
||||
['indexPatterns', { watchDepth: 'collection' }],
|
||||
['filters', { watchDepth: 'collection' }],
|
||||
|
||||
// All modifiers default to true.
|
||||
// Set to false to hide subcomponents.
|
||||
'showSearchBar',
|
||||
'showQueryBar',
|
||||
'showQueryInput',
|
||||
'showSaveQuery',
|
||||
'showDatePicker',
|
||||
'showFilterBar',
|
||||
|
||||
'appName',
|
||||
'screenTitle',
|
||||
'dateRangeFrom',
|
||||
'dateRangeTo',
|
||||
'savedQueryId',
|
||||
'isRefreshPaused',
|
||||
'refreshInterval',
|
||||
'disableAutoFocus',
|
||||
'showAutoRefreshOnly',
|
||||
|
||||
// temporary flag to use the stateful components
|
||||
'useDefaultBehaviors',
|
||||
]);
|
||||
};
|
||||
|
||||
let isLoaded = false;
|
||||
|
||||
export function loadKbnTopNavDirectives(navUi) {
|
||||
if (!isLoaded) {
|
||||
isLoaded = true;
|
||||
angular
|
||||
.module('kibana')
|
||||
.directive('kbnTopNav', createTopNavDirective)
|
||||
.directive('kbnTopNavHelper', createTopNavHelper(navUi));
|
||||
}
|
||||
}
|
|
@ -32,31 +32,8 @@ export const GlobalStateProvider: React.FC<GlobalStateProviderProps> = ({
|
|||
toasts,
|
||||
children,
|
||||
}) => {
|
||||
// TODO: remove fakeAngularRootScope and fakeAngularLocation when angular is removed
|
||||
const fakeAngularRootScope: Partial<ng.IRootScopeService> = {
|
||||
$on:
|
||||
(name: string, listener: (event: ng.IAngularEvent, ...args: any[]) => any): (() => void) =>
|
||||
() => {},
|
||||
$applyAsync: () => {},
|
||||
};
|
||||
|
||||
const fakeAngularLocation: Partial<ng.ILocationService> = {
|
||||
search: () => {
|
||||
return {} as any;
|
||||
},
|
||||
replace: () => {
|
||||
return {} as any;
|
||||
},
|
||||
};
|
||||
|
||||
const localState: State = {};
|
||||
const state = new GlobalState(
|
||||
query,
|
||||
toasts,
|
||||
fakeAngularRootScope,
|
||||
fakeAngularLocation,
|
||||
localState as { [key: string]: unknown }
|
||||
);
|
||||
const state = new GlobalState(query, toasts, localState as { [key: string]: unknown });
|
||||
|
||||
const initialState: any = state.getState();
|
||||
for (const key in initialState) {
|
||||
|
|
|
@ -44,7 +44,7 @@ const angularNoop = () => {
|
|||
export interface IShims {
|
||||
toastNotifications: CoreStart['notifications']['toasts'];
|
||||
capabilities: CoreStart['application']['capabilities'];
|
||||
getAngularInjector: typeof angularNoop | (() => angular.auto.IInjectorService);
|
||||
getAngularInjector: typeof angularNoop;
|
||||
getBasePath: () => string;
|
||||
getInjected: (name: string, defaultValue?: unknown) => unknown;
|
||||
breadcrumbs: {
|
||||
|
@ -73,23 +73,18 @@ export interface IShims {
|
|||
export class Legacy {
|
||||
private static _shims: IShims;
|
||||
|
||||
public static init(
|
||||
{
|
||||
core,
|
||||
data,
|
||||
isCloud,
|
||||
triggersActionsUi,
|
||||
usageCollection,
|
||||
appMountParameters,
|
||||
}: MonitoringStartPluginDependencies,
|
||||
ngInjector?: angular.auto.IInjectorService
|
||||
) {
|
||||
public static init({
|
||||
core,
|
||||
data,
|
||||
isCloud,
|
||||
triggersActionsUi,
|
||||
usageCollection,
|
||||
appMountParameters,
|
||||
}: MonitoringStartPluginDependencies) {
|
||||
this._shims = {
|
||||
toastNotifications: core.notifications.toasts,
|
||||
capabilities: core.application.capabilities,
|
||||
getAngularInjector: ngInjector
|
||||
? (): angular.auto.IInjectorService => ngInjector
|
||||
: angularNoop,
|
||||
getAngularInjector: angularNoop,
|
||||
getBasePath: (): string => core.http.basePath.get(),
|
||||
getInjected: (name: string, defaultValue?: unknown): string | unknown =>
|
||||
core.injectedMetadata.getInjectedVar(name, defaultValue),
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Subscription } from 'rxjs';
|
||||
import { History, createHashHistory } from 'history';
|
||||
import { MonitoringStartPluginDependencies } from './types';
|
||||
|
@ -27,10 +26,6 @@ import {
|
|||
withNotifyOnErrors,
|
||||
} from '../../../../src/plugins/kibana_utils/public';
|
||||
|
||||
interface Route {
|
||||
params: { _g: unknown };
|
||||
}
|
||||
|
||||
interface RawObject {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
@ -57,7 +52,6 @@ export interface MonitoringAppStateTransitions {
|
|||
const GLOBAL_STATE_KEY = '_g';
|
||||
const objectEquals = (objA: any, objB: any) => JSON.stringify(objA) === JSON.stringify(objB);
|
||||
|
||||
// TODO: clean all angular references after angular is removed
|
||||
export class GlobalState {
|
||||
private readonly stateSyncRef: ISyncStateRef;
|
||||
private readonly stateContainer: StateContainer<
|
||||
|
@ -70,13 +64,10 @@ export class GlobalState {
|
|||
private readonly timefilterRef: MonitoringStartPluginDependencies['data']['query']['timefilter']['timefilter'];
|
||||
|
||||
private lastAssignedState: MonitoringAppState = {};
|
||||
private lastKnownGlobalState?: string;
|
||||
|
||||
constructor(
|
||||
queryService: MonitoringStartPluginDependencies['data']['query'],
|
||||
toasts: MonitoringStartPluginDependencies['core']['notifications']['toasts'],
|
||||
rootScope: Partial<ng.IRootScopeService>,
|
||||
ngLocation: Partial<ng.ILocationService>,
|
||||
externalState: RawObject
|
||||
) {
|
||||
this.timefilterRef = queryService.timefilter.timefilter;
|
||||
|
@ -102,9 +93,6 @@ export class GlobalState {
|
|||
|
||||
this.stateContainerChangeSub = this.stateContainer.state$.subscribe(() => {
|
||||
this.lastAssignedState = this.getState();
|
||||
if (!this.stateContainer.get() && this.lastKnownGlobalState) {
|
||||
ngLocation.search?.(`${GLOBAL_STATE_KEY}=${this.lastKnownGlobalState}`).replace();
|
||||
}
|
||||
|
||||
// TODO: check if this is not needed after https://github.com/elastic/kibana/pull/109132 is merged
|
||||
if (Legacy.isInitializated()) {
|
||||
|
@ -112,15 +100,11 @@ export class GlobalState {
|
|||
}
|
||||
|
||||
this.syncExternalState(externalState);
|
||||
rootScope.$applyAsync?.();
|
||||
});
|
||||
|
||||
this.syncQueryStateWithUrlManager = syncQueryStateWithUrl(queryService, this.stateStorage);
|
||||
this.stateSyncRef.start();
|
||||
this.startHashSync(rootScope, ngLocation);
|
||||
this.lastAssignedState = this.getState();
|
||||
|
||||
rootScope.$on?.('$destroy', () => this.destroy());
|
||||
}
|
||||
|
||||
private syncExternalState(externalState: { [key: string]: unknown }) {
|
||||
|
@ -137,24 +121,6 @@ export class GlobalState {
|
|||
}
|
||||
}
|
||||
|
||||
private startHashSync(
|
||||
rootScope: Partial<ng.IRootScopeService>,
|
||||
ngLocation: Partial<ng.ILocationService>
|
||||
) {
|
||||
rootScope.$on?.(
|
||||
'$routeChangeStart',
|
||||
(_: { preventDefault: () => void }, newState: Route, oldState: Route) => {
|
||||
const currentGlobalState = oldState?.params?._g;
|
||||
const nextGlobalState = newState?.params?._g;
|
||||
if (!nextGlobalState && currentGlobalState && typeof currentGlobalState === 'string') {
|
||||
newState.params._g = currentGlobalState;
|
||||
ngLocation.search?.(`${GLOBAL_STATE_KEY}=${currentGlobalState}`).replace();
|
||||
}
|
||||
this.lastKnownGlobalState = (nextGlobalState || currentGlobalState) as string;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public setState(state?: { [key: string]: unknown }) {
|
||||
const currentAppState = this.getState();
|
||||
const newAppState = { ...currentAppState, ...state };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue