mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Angular: Option to disable angular support and isolate angular dependencies (#45421)
* Angular: Initial setting that disables angular, load angular support in separate chunk * Load angular panels on demand * Load alerting in separate chunk only when angularSupportEnabled * progress, do not export core_module if angular disabled * Progress * Update public/app/features/plugins/built_in_plugins.ts Co-authored-by: Ryan McKinley <ryantxu@gmail.com> * Removing remaining usage of angular from outside angular app (not counting plugins) * Update config and docs * Fix sample.ini * Update public/app/features/alerting/AlertTab.tsx Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com> * Fixing prettier issue Co-authored-by: Ryan McKinley <ryantxu@gmail.com> Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
This commit is contained in:
@@ -292,6 +292,9 @@ content_security_policy = false
|
||||
# $ROOT_PATH is server.root_url without the protocol.
|
||||
content_security_policy_template = """script-src 'self' 'unsafe-eval' 'unsafe-inline' 'strict-dynamic' $NONCE;object-src 'none';font-src 'self';style-src 'self' 'unsafe-inline' blob:;img-src * data:;base-uri 'self';connect-src 'self' grafana.com ws://$ROOT_PATH wss://$ROOT_PATH;manifest-src 'self';media-src 'none';form-action 'self';"""
|
||||
|
||||
# Controls if old angular plugins are supported or not. This will be disabled by default in Grafana v9.
|
||||
angular_support_enabled = true
|
||||
|
||||
#################################### Snapshots ###########################
|
||||
[snapshots]
|
||||
# snapshot sharing options
|
||||
|
||||
@@ -292,6 +292,9 @@
|
||||
# $ROOT_PATH is server.root_url without the protocol.
|
||||
;content_security_policy_template = """script-src 'self' 'unsafe-eval' 'unsafe-inline' 'strict-dynamic' $NONCE;object-src 'none';font-src 'self';style-src 'self' 'unsafe-inline' blob:;img-src * data:;base-uri 'self';connect-src 'self' grafana.com ws://$ROOT_PATH wss://$ROOT_PATH;manifest-src 'self';media-src 'none';form-action 'self';"""
|
||||
|
||||
# Controls if old angular plugins are supported or not. This will be disabled by default in Grafana v9.
|
||||
;angular_support_enabled = true
|
||||
|
||||
#################################### Snapshots ###########################
|
||||
[snapshots]
|
||||
# snapshot sharing options
|
||||
|
||||
@@ -582,6 +582,21 @@ Set Content Security Policy template used when adding the Content-Security-Polic
|
||||
|
||||
<hr />
|
||||
|
||||
### angular_support_enabled
|
||||
|
||||
This currently defaults to `true` but will in Grafana v9 default to `false`. When set to false the angular framework and support components will not be loaded. This means that
|
||||
all plugins and core features that depend on angular support will stop working.
|
||||
|
||||
Current core features that will stop working:
|
||||
|
||||
- Heatmap panel
|
||||
- Old graph panel
|
||||
- Old table panel
|
||||
- Postgres, MySQL and MSSQL data source query editors
|
||||
- Legacy alerting edit rule UI
|
||||
|
||||
Before we disable angular support by default we plan to migrate these remaining areas to React.
|
||||
|
||||
## [snapshots]
|
||||
|
||||
### external_enabled
|
||||
|
||||
@@ -138,4 +138,5 @@ export interface GrafanaConfig {
|
||||
geomapDefaultBaseLayer?: MapLayerOptions;
|
||||
geomapDisableCustomBaseLayer?: boolean;
|
||||
unifiedAlertingEnabled: boolean;
|
||||
angularSupportEnabled: boolean;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ export class GrafanaBootConfig implements GrafanaConfig {
|
||||
alertingErrorOrTimeout = '';
|
||||
alertingNoDataOrNullValues = '';
|
||||
alertingMinInterval = 1;
|
||||
angularSupportEnabled = false;
|
||||
authProxyEnabled = false;
|
||||
exploreEnabled = false;
|
||||
ldapEnabled = false;
|
||||
|
||||
@@ -229,6 +229,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
|
||||
"externalUserMngLinkUrl": setting.ExternalUserMngLinkUrl,
|
||||
"externalUserMngLinkName": setting.ExternalUserMngLinkName,
|
||||
"viewersCanEdit": setting.ViewersCanEdit,
|
||||
"angularSupportEnabled": hs.Cfg.AngularSupportEnabled,
|
||||
"editorsCanAdmin": hs.Cfg.EditorsCanAdmin,
|
||||
"disableSanitizeHtml": hs.Cfg.DisableSanitizeHtml,
|
||||
"pluginsToPreload": pluginsToPreload,
|
||||
|
||||
@@ -254,7 +254,8 @@ type Cfg struct {
|
||||
// CSPEnabled toggles Content Security Policy support.
|
||||
CSPEnabled bool
|
||||
// CSPTemplate contains the Content Security Policy template.
|
||||
CSPTemplate string
|
||||
CSPTemplate string
|
||||
AngularSupportEnabled bool
|
||||
|
||||
TempDataLifetime time.Duration
|
||||
PluginsEnableAlpha bool
|
||||
@@ -1191,6 +1192,7 @@ func readSecuritySettings(iniFile *ini.File, cfg *Cfg) error {
|
||||
cfg.StrictTransportSecuritySubDomains = security.Key("strict_transport_security_subdomains").MustBool(false)
|
||||
cfg.CSPEnabled = security.Key("content_security_policy").MustBool(false)
|
||||
cfg.CSPTemplate = security.Key("content_security_policy_template").MustString("")
|
||||
cfg.AngularSupportEnabled = security.Key("angular_support_enabled").MustBool(true)
|
||||
|
||||
// read data source proxy whitelist
|
||||
DataProxyWhiteList = make(map[string]bool)
|
||||
|
||||
@@ -15,15 +15,16 @@ import { GrafanaRoute } from './core/navigation/GrafanaRoute';
|
||||
import { AppNotificationList } from './core/components/AppNotifications/AppNotificationList';
|
||||
import { SearchWrapper } from 'app/features/search';
|
||||
import { LiveConnectionWarning } from './features/live/LiveConnectionWarning';
|
||||
import { AngularRoot } from './angular/AngularRoot';
|
||||
import { I18nProvider } from './core/localisation';
|
||||
import { AngularRoot } from './angular/AngularRoot';
|
||||
import { loadAndInitAngularIfEnabled } from './angular/loadAndInitAngularIfEnabled';
|
||||
|
||||
interface AppWrapperProps {
|
||||
app: GrafanaApp;
|
||||
}
|
||||
|
||||
interface AppWrapperState {
|
||||
ngInjector: any;
|
||||
ready?: boolean;
|
||||
}
|
||||
|
||||
/** Used by enterprise */
|
||||
@@ -39,27 +40,14 @@ export function addPageBanner(fn: ComponentType) {
|
||||
}
|
||||
|
||||
export class AppWrapper extends React.Component<AppWrapperProps, AppWrapperState> {
|
||||
container = React.createRef<HTMLDivElement>();
|
||||
|
||||
constructor(props: AppWrapperProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
ngInjector: null,
|
||||
};
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.container) {
|
||||
this.bootstrapNgApp();
|
||||
} else {
|
||||
throw new Error('Failed to boot angular app, no container to attach to');
|
||||
}
|
||||
}
|
||||
|
||||
bootstrapNgApp() {
|
||||
const injector = this.props.app.angularApp.bootstrap();
|
||||
this.setState({ ngInjector: injector });
|
||||
async componentDidMount() {
|
||||
await loadAndInitAngularIfEnabled();
|
||||
this.setState({ ready: true });
|
||||
$('.preloader').remove();
|
||||
}
|
||||
|
||||
@@ -91,6 +79,8 @@ export class AppWrapper extends React.Component<AppWrapperProps, AppWrapperState
|
||||
}
|
||||
|
||||
render() {
|
||||
const { ready } = this.state;
|
||||
|
||||
navigationLogger('AppWrapper', false, 'rendering');
|
||||
|
||||
const newNavigationEnabled = Boolean(config.featureToggles.newNavigation);
|
||||
@@ -111,10 +101,10 @@ export class AppWrapper extends React.Component<AppWrapperProps, AppWrapperState
|
||||
<Banner key={index.toString()} />
|
||||
))}
|
||||
|
||||
<AngularRoot ref={this.container} />
|
||||
<AngularRoot />
|
||||
<AppNotificationList />
|
||||
<SearchWrapper />
|
||||
{this.state.ngInjector && this.renderRoutes()}
|
||||
{ready && this.renderRoutes()}
|
||||
{bodyRenderHooks.map((Hook, index) => (
|
||||
<Hook key={index.toString()} />
|
||||
))}
|
||||
|
||||
@@ -14,6 +14,11 @@ import { extend } from 'lodash';
|
||||
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
import { getTemplateSrv } from '@grafana/runtime';
|
||||
import { registerComponents } from './registerComponents';
|
||||
import { exposeToPlugin } from 'app/features/plugins/plugin_loader';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import * as sdk from 'app/plugins/sdk';
|
||||
import { promiseToDigest } from './promiseToDigest';
|
||||
|
||||
export class AngularApp {
|
||||
ngModuleDependencies: any[];
|
||||
@@ -93,6 +98,18 @@ export class AngularApp {
|
||||
registerComponents();
|
||||
initAngularRoutingBridge();
|
||||
|
||||
// Angular plugins import this
|
||||
exposeToPlugin('angular', angular);
|
||||
exposeToPlugin('app/core/utils/promiseToDigest', { promiseToDigest, __esModule: true });
|
||||
exposeToPlugin('app/plugins/sdk', sdk);
|
||||
exposeToPlugin('app/core/core_module', coreModule);
|
||||
exposeToPlugin('app/core/core', {
|
||||
coreModule: coreModule,
|
||||
appEvents: appEvents,
|
||||
contextSrv: contextSrv,
|
||||
__esModule: true,
|
||||
});
|
||||
|
||||
// disable tool tip animation
|
||||
$.fn.tooltip.defaults.animation = false;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import './services/popover_srv';
|
||||
import './services/timer';
|
||||
import './services/AngularLoader';
|
||||
|
||||
import '../angular/jquery_extended';
|
||||
import './dropdown_typeahead';
|
||||
import './autofill_event_fix';
|
||||
import './metric_segment';
|
||||
@@ -37,3 +38,4 @@ import './components/plugin_component';
|
||||
import './GrafanaCtrl';
|
||||
|
||||
export { AngularApp } from './AngularApp';
|
||||
export { coreModule } from './core_module';
|
||||
|
||||
22
public/app/angular/loadAndInitAngularIfEnabled.ts
Normal file
22
public/app/angular/loadAndInitAngularIfEnabled.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { config, setAngularLoader } from '@grafana/runtime';
|
||||
|
||||
export async function loadAndInitAngularIfEnabled() {
|
||||
if (config.angularSupportEnabled) {
|
||||
const { AngularApp } = await import(/* webpackChunkName: "AngularApp" */ './index');
|
||||
const app = new AngularApp();
|
||||
app.init();
|
||||
app.bootstrap();
|
||||
} else {
|
||||
setAngularLoader({
|
||||
load: (elem, scopeProps, template) => {
|
||||
return {
|
||||
destroy: () => {},
|
||||
digest: () => {},
|
||||
getScope: () => {
|
||||
return {};
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,6 @@ import { setPanelRenderer } from '@grafana/runtime/src/components/PanelRenderer'
|
||||
import { PanelDataErrorView } from './features/panel/components/PanelDataErrorView';
|
||||
import { setPanelDataErrorView } from '@grafana/runtime/src/components/PanelDataErrorView';
|
||||
import { DatasourceSrv } from './features/plugins/datasource_srv';
|
||||
import { AngularApp } from './angular';
|
||||
import { ModalManager } from './core/services/ModalManager';
|
||||
import { initWindowRuntime } from './features/runtime/init';
|
||||
import { createQueryVariableAdapter } from './features/variables/query/adapter';
|
||||
@@ -89,12 +88,6 @@ if (process.env.NODE_ENV === 'development') {
|
||||
}
|
||||
|
||||
export class GrafanaApp {
|
||||
angularApp: AngularApp;
|
||||
|
||||
constructor() {
|
||||
this.angularApp = new AngularApp();
|
||||
}
|
||||
|
||||
async init() {
|
||||
try {
|
||||
setBackendSrv(backendSrv);
|
||||
@@ -148,9 +141,6 @@ export class GrafanaApp {
|
||||
const modalManager = new ModalManager();
|
||||
modalManager.init();
|
||||
|
||||
// Init angular
|
||||
this.angularApp.init();
|
||||
|
||||
// Preload selected app plugins
|
||||
await preloadPlugins(config.pluginsToPreload);
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import './jquery_extended';
|
||||
import './services/search_srv';
|
||||
import { colors, JsonExplorer } from '@grafana/ui/';
|
||||
import appEvents from './app_events';
|
||||
import { assignModelProperties } from './utils/model_utils';
|
||||
|
||||
@@ -2,13 +2,10 @@ import React, { PureComponent } from 'react';
|
||||
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux';
|
||||
import { Alert, Button, ConfirmModal, Container, CustomScrollbar, HorizontalGroup, IconName, Modal } from '@grafana/ui';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { AngularComponent, getAngularLoader, getDataSourceSrv } from '@grafana/runtime';
|
||||
import { AngularComponent, config, getAngularLoader, getDataSourceSrv } from '@grafana/runtime';
|
||||
import { getAlertingValidationMessage } from './getAlertingValidationMessage';
|
||||
|
||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||
import StateHistory from './StateHistory';
|
||||
import 'app/features/alerting/AlertTabCtrl';
|
||||
|
||||
import { DashboardModel } from '../dashboard/state/DashboardModel';
|
||||
import { PanelModel } from '../dashboard/state/PanelModel';
|
||||
import { TestRuleResult } from './TestRuleResult';
|
||||
@@ -57,8 +54,14 @@ class UnConnectedAlertTab extends PureComponent<Props, State> {
|
||||
showTestRule: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.loadAlertTab();
|
||||
async componentDidMount() {
|
||||
if (config.angularSupportEnabled) {
|
||||
await import(/* webpackChunkName: "AlertTabCtrl" */ 'app/features/alerting/AlertTabCtrl');
|
||||
this.loadAlertTab();
|
||||
} else {
|
||||
// TODO probably need to migrate AlertTab to react
|
||||
alert('Angular support disabled, legacy alerting cannot function without angular support');
|
||||
}
|
||||
}
|
||||
|
||||
onAngularPanelUpdated = () => {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { isArray } from 'angular';
|
||||
import {
|
||||
AlertManagerCortexConfig,
|
||||
GrafanaManagedReceiverConfig,
|
||||
@@ -6,6 +5,7 @@ import {
|
||||
Route,
|
||||
} from 'app/plugins/datasource/alertmanager/types';
|
||||
import { CloudNotifierType, NotifierDTO, NotifierType } from 'app/types';
|
||||
import { isArray } from 'lodash';
|
||||
import {
|
||||
CloudChannelConfig,
|
||||
CloudChannelMap,
|
||||
|
||||
@@ -5,7 +5,6 @@ import React, { useEffect, useState } from 'react';
|
||||
import { Prompt } from 'react-router-dom';
|
||||
import { DashboardModel } from '../../state/DashboardModel';
|
||||
import { each, filter, find } from 'lodash';
|
||||
import angular from 'angular';
|
||||
import { UnsavedChangesModal } from '../SaveDashboard/UnsavedChangesModal';
|
||||
import * as H from 'history';
|
||||
import { SaveLibraryPanelModal } from 'app/features/library-panels/components/SaveLibraryPanelModal/SaveLibraryPanelModal';
|
||||
@@ -248,8 +247,8 @@ export function hasChanges(current: DashboardModel, original: any) {
|
||||
currentTimepicker.now = originalTimepicker.now;
|
||||
}
|
||||
|
||||
const currentJson = angular.toJson(currentClean);
|
||||
const originalJson = angular.toJson(originalClean);
|
||||
const currentJson = JSON.stringify(currentClean, null);
|
||||
const originalJson = JSON.stringify(originalClean, null);
|
||||
|
||||
return currentJson !== originalJson;
|
||||
}
|
||||
|
||||
@@ -45,15 +45,12 @@ import * as timeseriesPanel from 'app/plugins/panel/timeseries/module';
|
||||
import * as stateTimelinePanel from 'app/plugins/panel/state-timeline/module';
|
||||
import * as statusHistoryPanel from 'app/plugins/panel/status-history/module';
|
||||
import * as candlestickPanel from 'app/plugins/panel/candlestick/module';
|
||||
import * as graphPanel from 'app/plugins/panel/graph/module';
|
||||
import * as xyChartPanel from 'app/plugins/panel/xychart/module';
|
||||
import * as dashListPanel from 'app/plugins/panel/dashlist/module';
|
||||
import * as pluginsListPanel from 'app/plugins/panel/pluginlist/module';
|
||||
import * as alertListPanel from 'app/plugins/panel/alertlist/module';
|
||||
import * as annoListPanel from 'app/plugins/panel/annolist/module';
|
||||
import * as heatmapPanel from 'app/plugins/panel/heatmap/module';
|
||||
import * as tablePanel from 'app/plugins/panel/table/module';
|
||||
import * as oldTablePanel from 'app/plugins/panel/table-old/module';
|
||||
import * as statPanel from 'app/plugins/panel/stat/module';
|
||||
import * as gettingStartedPanel from 'app/plugins/panel/gettingstarted/module';
|
||||
import * as gaugePanel from 'app/plugins/panel/gauge/module';
|
||||
@@ -73,6 +70,11 @@ import * as alertGroupsPanel from 'app/plugins/panel/alertGroups/module';
|
||||
const geomapPanel = async () => await import(/* webpackChunkName: "geomapPanel" */ 'app/plugins/panel/geomap/module');
|
||||
const canvasPanel = async () => await import(/* webpackChunkName: "canvasPanel" */ 'app/plugins/panel/canvas/module');
|
||||
const iconPanel = async () => await import(/* webpackChunkName: "iconPanel" */ 'app/plugins/panel/icon/module');
|
||||
const graphPanel = async () => await import(/* webpackChunkName: "graphPlugin" */ 'app/plugins/panel/graph/module');
|
||||
const heatmapPanel = async () =>
|
||||
await import(/* webpackChunkName: "heatmapPlugin" */ 'app/plugins/panel/heatmap/module');
|
||||
const tableOldPanel = async () =>
|
||||
await import(/* webpackChunkName: "tableOldPlugin" */ 'app/plugins/panel/table-old/module');
|
||||
|
||||
const builtInPlugins: any = {
|
||||
'app/plugins/datasource/graphite/module': graphitePlugin,
|
||||
@@ -112,7 +114,7 @@ const builtInPlugins: any = {
|
||||
'app/plugins/panel/annolist/module': annoListPanel,
|
||||
'app/plugins/panel/heatmap/module': heatmapPanel,
|
||||
'app/plugins/panel/table/module': tablePanel,
|
||||
'app/plugins/panel/table-old/module': oldTablePanel,
|
||||
'app/plugins/panel/table-old/module': tableOldPanel,
|
||||
'app/plugins/panel/news/module': newsPanel,
|
||||
'app/plugins/panel/live/module': livePanel,
|
||||
'app/plugins/panel/stat/module': statPanel,
|
||||
|
||||
@@ -28,25 +28,22 @@ export function importPanelPluginFromMeta(meta: grafanaData.PanelPluginMeta): Pr
|
||||
return getPanelPlugin(meta);
|
||||
}
|
||||
|
||||
function getPanelPlugin(meta: grafanaData.PanelPluginMeta): Promise<grafanaData.PanelPlugin> {
|
||||
return importPluginModule(meta.module, meta.info?.version)
|
||||
.then((pluginExports) => {
|
||||
if (pluginExports.plugin) {
|
||||
return pluginExports.plugin as grafanaData.PanelPlugin;
|
||||
} else if (pluginExports.PanelCtrl) {
|
||||
const plugin = new grafanaData.PanelPlugin(null);
|
||||
plugin.angularPanelCtrl = pluginExports.PanelCtrl;
|
||||
return plugin;
|
||||
}
|
||||
throw new Error('missing export: plugin or PanelCtrl');
|
||||
})
|
||||
.then((plugin) => {
|
||||
plugin.meta = meta;
|
||||
return plugin;
|
||||
})
|
||||
.catch((err) => {
|
||||
// TODO, maybe a different error plugin
|
||||
console.warn('Error loading panel plugin: ' + meta.id, err);
|
||||
return getPanelPluginLoadError(meta, err);
|
||||
});
|
||||
async function getPanelPlugin(meta: grafanaData.PanelPluginMeta): Promise<grafanaData.PanelPlugin> {
|
||||
try {
|
||||
const pluginExports = await importPluginModule(meta.module, meta.info?.version);
|
||||
let plugin = pluginExports.plugin;
|
||||
|
||||
if (!plugin && pluginExports.PanelCtrl) {
|
||||
plugin = new grafanaData.PanelPlugin(null);
|
||||
plugin.angularPanelCtrl = pluginExports.PanelCtrl;
|
||||
}
|
||||
|
||||
plugin.meta = meta;
|
||||
|
||||
return plugin;
|
||||
} catch (err) {
|
||||
// TODO, maybe a different error plugin
|
||||
console.warn('Error loading panel plugin: ' + meta.id, err);
|
||||
return getPanelPluginLoadError(meta, err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
// eslint-disable-next-line lodash/import-scope
|
||||
import _ from 'lodash';
|
||||
import * as sdk from 'app/plugins/sdk';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import moment from 'moment'; // eslint-disable-line no-restricted-imports
|
||||
import angular from 'angular';
|
||||
import jquery from 'jquery';
|
||||
|
||||
// Experimental module exports
|
||||
@@ -21,12 +19,10 @@ import * as redux from 'redux';
|
||||
import config from 'app/core/config';
|
||||
import TimeSeries from 'app/core/time_series2';
|
||||
import TableModel from 'app/core/table_model';
|
||||
import { coreModule } from 'app/angular/core_module';
|
||||
import { appEvents, contextSrv } from 'app/core/core';
|
||||
import * as flatten from 'app/core/utils/flatten';
|
||||
import * as ticks from 'app/core/utils/ticks';
|
||||
import { BackendSrv, getBackendSrv } from 'app/core/services/backend_srv';
|
||||
import { promiseToDigest } from 'app/angular/promiseToDigest';
|
||||
import impressionSrv from 'app/core/services/impression_srv';
|
||||
import builtInPlugins from './built_in_plugins';
|
||||
import * as d3 from 'd3';
|
||||
@@ -75,7 +71,7 @@ grafanaRuntime.SystemJS.config({
|
||||
},
|
||||
});
|
||||
|
||||
function exposeToPlugin(name: string, component: any) {
|
||||
export function exposeToPlugin(name: string, component: any) {
|
||||
grafanaRuntime.SystemJS.registerDynamic(name, [], true, (require: any, exports: any, module: { exports: any }) => {
|
||||
module.exports = component;
|
||||
});
|
||||
@@ -87,7 +83,6 @@ exposeToPlugin('@grafana/runtime', grafanaRuntime);
|
||||
exposeToPlugin('lodash', _);
|
||||
exposeToPlugin('moment', moment);
|
||||
exposeToPlugin('jquery', jquery);
|
||||
exposeToPlugin('angular', angular);
|
||||
exposeToPlugin('d3', d3);
|
||||
exposeToPlugin('rxjs', rxjs);
|
||||
exposeToPlugin('rxjs/operators', rxjsOperators);
|
||||
@@ -120,24 +115,16 @@ exposeToPlugin('app/core/services/backend_srv', {
|
||||
getBackendSrv,
|
||||
});
|
||||
|
||||
exposeToPlugin('app/plugins/sdk', sdk);
|
||||
exposeToPlugin('app/core/utils/datemath', grafanaData.dateMath);
|
||||
exposeToPlugin('app/core/utils/flatten', flatten);
|
||||
exposeToPlugin('app/core/utils/kbn', kbn);
|
||||
exposeToPlugin('app/core/utils/ticks', ticks);
|
||||
exposeToPlugin('app/core/utils/promiseToDigest', {
|
||||
promiseToDigest: promiseToDigest,
|
||||
__esModule: true,
|
||||
});
|
||||
|
||||
exposeToPlugin('app/core/config', config);
|
||||
exposeToPlugin('app/core/time_series', TimeSeries);
|
||||
exposeToPlugin('app/core/time_series2', TimeSeries);
|
||||
exposeToPlugin('app/core/table_model', TableModel);
|
||||
exposeToPlugin('app/core/app_events', appEvents);
|
||||
exposeToPlugin('app/core/core_module', coreModule);
|
||||
exposeToPlugin('app/core/core', {
|
||||
coreModule: coreModule,
|
||||
appEvents: appEvents,
|
||||
contextSrv: contextSrv,
|
||||
__esModule: true,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import angular from 'angular';
|
||||
import { castArray, isEqual } from 'lodash';
|
||||
import {
|
||||
DataQuery,
|
||||
@@ -601,7 +600,7 @@ const timeRangeUpdated =
|
||||
const updatedVariable = getVariable<VariableWithOptions>(identifier.id, getState());
|
||||
const updatedOptions = updatedVariable.options;
|
||||
|
||||
if (angular.toJson(previousOptions) !== angular.toJson(updatedOptions)) {
|
||||
if (JSON.stringify(previousOptions) !== JSON.stringify(updatedOptions)) {
|
||||
const dashboard = getState().dashboard.getModel();
|
||||
dashboard?.templateVariableValueUpdated();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { auto } from 'angular';
|
||||
|
||||
export class QueryCtrl {
|
||||
target: any;
|
||||
datasource: any;
|
||||
@@ -8,7 +6,7 @@ export class QueryCtrl {
|
||||
hasRawMode = false;
|
||||
error = '';
|
||||
|
||||
constructor(public $scope: any, _$injector: auto.IInjectorService) {
|
||||
constructor(public $scope: any) {
|
||||
this.panelCtrl = this.panelCtrl || { panel: {} };
|
||||
this.target = this.target || { target: '' };
|
||||
this.panel = this.panelCtrl.panel;
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { IScope } from 'angular';
|
||||
|
||||
export interface Scope extends IScope {
|
||||
[key: string]: any;
|
||||
}
|
||||
@@ -15,7 +15,6 @@ export * from './explore';
|
||||
export * from './store';
|
||||
export * from './ldap';
|
||||
export * from './appEvent';
|
||||
export * from './angular';
|
||||
export * from './query';
|
||||
export * from './preferences';
|
||||
export * from './accessControl';
|
||||
|
||||
Reference in New Issue
Block a user