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>
pull/45485/head
Torkel Ödegaard 4 years ago committed by GitHub
parent 8bb3de3037
commit 2b9e46d1f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      conf/defaults.ini
  2. 3
      conf/sample.ini
  3. 15
      docs/sources/administration/configuration.md
  4. 1
      packages/grafana-data/src/types/config.ts
  5. 1
      packages/grafana-runtime/src/config.ts
  6. 1
      pkg/api/frontendsettings.go
  7. 4
      pkg/setting/setting.go
  8. 32
      public/app/AppWrapper.tsx
  9. 17
      public/app/angular/AngularApp.ts
  10. 2
      public/app/angular/index.ts
  11. 0
      public/app/angular/jquery_extended.ts
  12. 22
      public/app/angular/loadAndInitAngularIfEnabled.ts
  13. 10
      public/app/app.ts
  14. 2
      public/app/core/core.ts
  15. 15
      public/app/features/alerting/AlertTab.tsx
  16. 2
      public/app/features/alerting/unified/utils/receiver-form.ts
  17. 5
      public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx
  18. 10
      public/app/features/plugins/built_in_plugins.ts
  19. 39
      public/app/features/plugins/importPanelPlugin.ts
  20. 15
      public/app/features/plugins/plugin_loader.ts
  21. 3
      public/app/features/variables/state/actions.ts
  22. 4
      public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/query_ctrl.ts
  23. 5
      public/app/types/angular.ts
  24. 1
      public/app/types/index.ts

@ -292,6 +292,9 @@ content_security_policy = false
# $ROOT_PATH is server.root_url without the protocol. # $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';""" 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 ###########################
[snapshots] [snapshots]
# snapshot sharing options # snapshot sharing options

@ -292,6 +292,9 @@
# $ROOT_PATH is server.root_url without the protocol. # $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';""" ;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 ###########################
[snapshots] [snapshots]
# snapshot sharing options # snapshot sharing options

@ -582,6 +582,21 @@ Set Content Security Policy template used when adding the Content-Security-Polic
<hr /> <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] ## [snapshots]
### external_enabled ### external_enabled

@ -138,4 +138,5 @@ export interface GrafanaConfig {
geomapDefaultBaseLayer?: MapLayerOptions; geomapDefaultBaseLayer?: MapLayerOptions;
geomapDisableCustomBaseLayer?: boolean; geomapDisableCustomBaseLayer?: boolean;
unifiedAlertingEnabled: boolean; unifiedAlertingEnabled: boolean;
angularSupportEnabled: boolean;
} }

@ -41,6 +41,7 @@ export class GrafanaBootConfig implements GrafanaConfig {
alertingErrorOrTimeout = ''; alertingErrorOrTimeout = '';
alertingNoDataOrNullValues = ''; alertingNoDataOrNullValues = '';
alertingMinInterval = 1; alertingMinInterval = 1;
angularSupportEnabled = false;
authProxyEnabled = false; authProxyEnabled = false;
exploreEnabled = false; exploreEnabled = false;
ldapEnabled = false; ldapEnabled = false;

@ -229,6 +229,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
"externalUserMngLinkUrl": setting.ExternalUserMngLinkUrl, "externalUserMngLinkUrl": setting.ExternalUserMngLinkUrl,
"externalUserMngLinkName": setting.ExternalUserMngLinkName, "externalUserMngLinkName": setting.ExternalUserMngLinkName,
"viewersCanEdit": setting.ViewersCanEdit, "viewersCanEdit": setting.ViewersCanEdit,
"angularSupportEnabled": hs.Cfg.AngularSupportEnabled,
"editorsCanAdmin": hs.Cfg.EditorsCanAdmin, "editorsCanAdmin": hs.Cfg.EditorsCanAdmin,
"disableSanitizeHtml": hs.Cfg.DisableSanitizeHtml, "disableSanitizeHtml": hs.Cfg.DisableSanitizeHtml,
"pluginsToPreload": pluginsToPreload, "pluginsToPreload": pluginsToPreload,

@ -254,7 +254,8 @@ type Cfg struct {
// CSPEnabled toggles Content Security Policy support. // CSPEnabled toggles Content Security Policy support.
CSPEnabled bool CSPEnabled bool
// CSPTemplate contains the Content Security Policy template. // CSPTemplate contains the Content Security Policy template.
CSPTemplate string CSPTemplate string
AngularSupportEnabled bool
TempDataLifetime time.Duration TempDataLifetime time.Duration
PluginsEnableAlpha bool 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.StrictTransportSecuritySubDomains = security.Key("strict_transport_security_subdomains").MustBool(false)
cfg.CSPEnabled = security.Key("content_security_policy").MustBool(false) cfg.CSPEnabled = security.Key("content_security_policy").MustBool(false)
cfg.CSPTemplate = security.Key("content_security_policy_template").MustString("") cfg.CSPTemplate = security.Key("content_security_policy_template").MustString("")
cfg.AngularSupportEnabled = security.Key("angular_support_enabled").MustBool(true)
// read data source proxy whitelist // read data source proxy whitelist
DataProxyWhiteList = make(map[string]bool) DataProxyWhiteList = make(map[string]bool)

@ -15,15 +15,16 @@ import { GrafanaRoute } from './core/navigation/GrafanaRoute';
import { AppNotificationList } from './core/components/AppNotifications/AppNotificationList'; import { AppNotificationList } from './core/components/AppNotifications/AppNotificationList';
import { SearchWrapper } from 'app/features/search'; import { SearchWrapper } from 'app/features/search';
import { LiveConnectionWarning } from './features/live/LiveConnectionWarning'; import { LiveConnectionWarning } from './features/live/LiveConnectionWarning';
import { AngularRoot } from './angular/AngularRoot';
import { I18nProvider } from './core/localisation'; import { I18nProvider } from './core/localisation';
import { AngularRoot } from './angular/AngularRoot';
import { loadAndInitAngularIfEnabled } from './angular/loadAndInitAngularIfEnabled';
interface AppWrapperProps { interface AppWrapperProps {
app: GrafanaApp; app: GrafanaApp;
} }
interface AppWrapperState { interface AppWrapperState {
ngInjector: any; ready?: boolean;
} }
/** Used by enterprise */ /** Used by enterprise */
@ -39,27 +40,14 @@ export function addPageBanner(fn: ComponentType) {
} }
export class AppWrapper extends React.Component<AppWrapperProps, AppWrapperState> { export class AppWrapper extends React.Component<AppWrapperProps, AppWrapperState> {
container = React.createRef<HTMLDivElement>();
constructor(props: AppWrapperProps) { constructor(props: AppWrapperProps) {
super(props); super(props);
this.state = {};
this.state = {
ngInjector: null,
};
} }
componentDidMount() { async componentDidMount() {
if (this.container) { await loadAndInitAngularIfEnabled();
this.bootstrapNgApp(); this.setState({ ready: true });
} 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 });
$('.preloader').remove(); $('.preloader').remove();
} }
@ -91,6 +79,8 @@ export class AppWrapper extends React.Component<AppWrapperProps, AppWrapperState
} }
render() { render() {
const { ready } = this.state;
navigationLogger('AppWrapper', false, 'rendering'); navigationLogger('AppWrapper', false, 'rendering');
const newNavigationEnabled = Boolean(config.featureToggles.newNavigation); const newNavigationEnabled = Boolean(config.featureToggles.newNavigation);
@ -111,10 +101,10 @@ export class AppWrapper extends React.Component<AppWrapperProps, AppWrapperState
<Banner key={index.toString()} /> <Banner key={index.toString()} />
))} ))}
<AngularRoot ref={this.container} /> <AngularRoot />
<AppNotificationList /> <AppNotificationList />
<SearchWrapper /> <SearchWrapper />
{this.state.ngInjector && this.renderRoutes()} {ready && this.renderRoutes()}
{bodyRenderHooks.map((Hook, index) => ( {bodyRenderHooks.map((Hook, index) => (
<Hook key={index.toString()} /> <Hook key={index.toString()} />
))} ))}

@ -14,6 +14,11 @@ import { extend } from 'lodash';
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { getTemplateSrv } from '@grafana/runtime'; import { getTemplateSrv } from '@grafana/runtime';
import { registerComponents } from './registerComponents'; 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 { export class AngularApp {
ngModuleDependencies: any[]; ngModuleDependencies: any[];
@ -93,6 +98,18 @@ export class AngularApp {
registerComponents(); registerComponents();
initAngularRoutingBridge(); 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 // disable tool tip animation
$.fn.tooltip.defaults.animation = false; $.fn.tooltip.defaults.animation = false;
} }

@ -9,6 +9,7 @@ import './services/popover_srv';
import './services/timer'; import './services/timer';
import './services/AngularLoader'; import './services/AngularLoader';
import '../angular/jquery_extended';
import './dropdown_typeahead'; import './dropdown_typeahead';
import './autofill_event_fix'; import './autofill_event_fix';
import './metric_segment'; import './metric_segment';
@ -37,3 +38,4 @@ import './components/plugin_component';
import './GrafanaCtrl'; import './GrafanaCtrl';
export { AngularApp } from './AngularApp'; export { AngularApp } from './AngularApp';
export { coreModule } from './core_module';

@ -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 { PanelDataErrorView } from './features/panel/components/PanelDataErrorView';
import { setPanelDataErrorView } from '@grafana/runtime/src/components/PanelDataErrorView'; import { setPanelDataErrorView } from '@grafana/runtime/src/components/PanelDataErrorView';
import { DatasourceSrv } from './features/plugins/datasource_srv'; import { DatasourceSrv } from './features/plugins/datasource_srv';
import { AngularApp } from './angular';
import { ModalManager } from './core/services/ModalManager'; import { ModalManager } from './core/services/ModalManager';
import { initWindowRuntime } from './features/runtime/init'; import { initWindowRuntime } from './features/runtime/init';
import { createQueryVariableAdapter } from './features/variables/query/adapter'; import { createQueryVariableAdapter } from './features/variables/query/adapter';
@ -89,12 +88,6 @@ if (process.env.NODE_ENV === 'development') {
} }
export class GrafanaApp { export class GrafanaApp {
angularApp: AngularApp;
constructor() {
this.angularApp = new AngularApp();
}
async init() { async init() {
try { try {
setBackendSrv(backendSrv); setBackendSrv(backendSrv);
@ -148,9 +141,6 @@ export class GrafanaApp {
const modalManager = new ModalManager(); const modalManager = new ModalManager();
modalManager.init(); modalManager.init();
// Init angular
this.angularApp.init();
// Preload selected app plugins // Preload selected app plugins
await preloadPlugins(config.pluginsToPreload); await preloadPlugins(config.pluginsToPreload);

@ -1,5 +1,3 @@
import './jquery_extended';
import './services/search_srv';
import { colors, JsonExplorer } from '@grafana/ui/'; import { colors, JsonExplorer } from '@grafana/ui/';
import appEvents from './app_events'; import appEvents from './app_events';
import { assignModelProperties } from './utils/model_utils'; import { assignModelProperties } from './utils/model_utils';

@ -2,13 +2,10 @@ import React, { PureComponent } from 'react';
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux'; import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux';
import { Alert, Button, ConfirmModal, Container, CustomScrollbar, HorizontalGroup, IconName, Modal } from '@grafana/ui'; import { Alert, Button, ConfirmModal, Container, CustomScrollbar, HorizontalGroup, IconName, Modal } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors'; 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 { getAlertingValidationMessage } from './getAlertingValidationMessage';
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
import StateHistory from './StateHistory'; import StateHistory from './StateHistory';
import 'app/features/alerting/AlertTabCtrl';
import { DashboardModel } from '../dashboard/state/DashboardModel'; import { DashboardModel } from '../dashboard/state/DashboardModel';
import { PanelModel } from '../dashboard/state/PanelModel'; import { PanelModel } from '../dashboard/state/PanelModel';
import { TestRuleResult } from './TestRuleResult'; import { TestRuleResult } from './TestRuleResult';
@ -57,8 +54,14 @@ class UnConnectedAlertTab extends PureComponent<Props, State> {
showTestRule: false, showTestRule: false,
}; };
componentDidMount() { async componentDidMount() {
this.loadAlertTab(); 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 = () => { onAngularPanelUpdated = () => {

@ -1,4 +1,3 @@
import { isArray } from 'angular';
import { import {
AlertManagerCortexConfig, AlertManagerCortexConfig,
GrafanaManagedReceiverConfig, GrafanaManagedReceiverConfig,
@ -6,6 +5,7 @@ import {
Route, Route,
} from 'app/plugins/datasource/alertmanager/types'; } from 'app/plugins/datasource/alertmanager/types';
import { CloudNotifierType, NotifierDTO, NotifierType } from 'app/types'; import { CloudNotifierType, NotifierDTO, NotifierType } from 'app/types';
import { isArray } from 'lodash';
import { import {
CloudChannelConfig, CloudChannelConfig,
CloudChannelMap, CloudChannelMap,

@ -5,7 +5,6 @@ import React, { useEffect, useState } from 'react';
import { Prompt } from 'react-router-dom'; import { Prompt } from 'react-router-dom';
import { DashboardModel } from '../../state/DashboardModel'; import { DashboardModel } from '../../state/DashboardModel';
import { each, filter, find } from 'lodash'; import { each, filter, find } from 'lodash';
import angular from 'angular';
import { UnsavedChangesModal } from '../SaveDashboard/UnsavedChangesModal'; import { UnsavedChangesModal } from '../SaveDashboard/UnsavedChangesModal';
import * as H from 'history'; import * as H from 'history';
import { SaveLibraryPanelModal } from 'app/features/library-panels/components/SaveLibraryPanelModal/SaveLibraryPanelModal'; 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; currentTimepicker.now = originalTimepicker.now;
} }
const currentJson = angular.toJson(currentClean); const currentJson = JSON.stringify(currentClean, null);
const originalJson = angular.toJson(originalClean); const originalJson = JSON.stringify(originalClean, null);
return currentJson !== originalJson; 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 stateTimelinePanel from 'app/plugins/panel/state-timeline/module';
import * as statusHistoryPanel from 'app/plugins/panel/status-history/module'; import * as statusHistoryPanel from 'app/plugins/panel/status-history/module';
import * as candlestickPanel from 'app/plugins/panel/candlestick/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 xyChartPanel from 'app/plugins/panel/xychart/module';
import * as dashListPanel from 'app/plugins/panel/dashlist/module'; import * as dashListPanel from 'app/plugins/panel/dashlist/module';
import * as pluginsListPanel from 'app/plugins/panel/pluginlist/module'; import * as pluginsListPanel from 'app/plugins/panel/pluginlist/module';
import * as alertListPanel from 'app/plugins/panel/alertlist/module'; import * as alertListPanel from 'app/plugins/panel/alertlist/module';
import * as annoListPanel from 'app/plugins/panel/annolist/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 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 statPanel from 'app/plugins/panel/stat/module';
import * as gettingStartedPanel from 'app/plugins/panel/gettingstarted/module'; import * as gettingStartedPanel from 'app/plugins/panel/gettingstarted/module';
import * as gaugePanel from 'app/plugins/panel/gauge/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 geomapPanel = async () => await import(/* webpackChunkName: "geomapPanel" */ 'app/plugins/panel/geomap/module');
const canvasPanel = async () => await import(/* webpackChunkName: "canvasPanel" */ 'app/plugins/panel/canvas/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 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 = { const builtInPlugins: any = {
'app/plugins/datasource/graphite/module': graphitePlugin, 'app/plugins/datasource/graphite/module': graphitePlugin,
@ -112,7 +114,7 @@ const builtInPlugins: any = {
'app/plugins/panel/annolist/module': annoListPanel, 'app/plugins/panel/annolist/module': annoListPanel,
'app/plugins/panel/heatmap/module': heatmapPanel, 'app/plugins/panel/heatmap/module': heatmapPanel,
'app/plugins/panel/table/module': tablePanel, '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/news/module': newsPanel,
'app/plugins/panel/live/module': livePanel, 'app/plugins/panel/live/module': livePanel,
'app/plugins/panel/stat/module': statPanel, 'app/plugins/panel/stat/module': statPanel,

@ -28,25 +28,22 @@ export function importPanelPluginFromMeta(meta: grafanaData.PanelPluginMeta): Pr
return getPanelPlugin(meta); return getPanelPlugin(meta);
} }
function getPanelPlugin(meta: grafanaData.PanelPluginMeta): Promise<grafanaData.PanelPlugin> { async function getPanelPlugin(meta: grafanaData.PanelPluginMeta): Promise<grafanaData.PanelPlugin> {
return importPluginModule(meta.module, meta.info?.version) try {
.then((pluginExports) => { const pluginExports = await importPluginModule(meta.module, meta.info?.version);
if (pluginExports.plugin) { let plugin = pluginExports.plugin;
return pluginExports.plugin as grafanaData.PanelPlugin;
} else if (pluginExports.PanelCtrl) { if (!plugin && pluginExports.PanelCtrl) {
const plugin = new grafanaData.PanelPlugin(null); plugin = new grafanaData.PanelPlugin(null);
plugin.angularPanelCtrl = pluginExports.PanelCtrl; plugin.angularPanelCtrl = pluginExports.PanelCtrl;
return plugin; }
}
throw new Error('missing export: plugin or PanelCtrl'); plugin.meta = meta;
})
.then((plugin) => { return plugin;
plugin.meta = meta; } catch (err) {
return plugin; // TODO, maybe a different error plugin
}) console.warn('Error loading panel plugin: ' + meta.id, err);
.catch((err) => { return getPanelPluginLoadError(meta, 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 // eslint-disable-next-line lodash/import-scope
import _ from 'lodash'; import _ from 'lodash';
import * as sdk from 'app/plugins/sdk';
import kbn from 'app/core/utils/kbn'; import kbn from 'app/core/utils/kbn';
import moment from 'moment'; // eslint-disable-line no-restricted-imports import moment from 'moment'; // eslint-disable-line no-restricted-imports
import angular from 'angular';
import jquery from 'jquery'; import jquery from 'jquery';
// Experimental module exports // Experimental module exports
@ -21,12 +19,10 @@ import * as redux from 'redux';
import config from 'app/core/config'; import config from 'app/core/config';
import TimeSeries from 'app/core/time_series2'; import TimeSeries from 'app/core/time_series2';
import TableModel from 'app/core/table_model'; import TableModel from 'app/core/table_model';
import { coreModule } from 'app/angular/core_module';
import { appEvents, contextSrv } from 'app/core/core'; import { appEvents, contextSrv } from 'app/core/core';
import * as flatten from 'app/core/utils/flatten'; import * as flatten from 'app/core/utils/flatten';
import * as ticks from 'app/core/utils/ticks'; import * as ticks from 'app/core/utils/ticks';
import { BackendSrv, getBackendSrv } from 'app/core/services/backend_srv'; import { BackendSrv, getBackendSrv } from 'app/core/services/backend_srv';
import { promiseToDigest } from 'app/angular/promiseToDigest';
import impressionSrv from 'app/core/services/impression_srv'; import impressionSrv from 'app/core/services/impression_srv';
import builtInPlugins from './built_in_plugins'; import builtInPlugins from './built_in_plugins';
import * as d3 from 'd3'; 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 }) => { grafanaRuntime.SystemJS.registerDynamic(name, [], true, (require: any, exports: any, module: { exports: any }) => {
module.exports = component; module.exports = component;
}); });
@ -87,7 +83,6 @@ exposeToPlugin('@grafana/runtime', grafanaRuntime);
exposeToPlugin('lodash', _); exposeToPlugin('lodash', _);
exposeToPlugin('moment', moment); exposeToPlugin('moment', moment);
exposeToPlugin('jquery', jquery); exposeToPlugin('jquery', jquery);
exposeToPlugin('angular', angular);
exposeToPlugin('d3', d3); exposeToPlugin('d3', d3);
exposeToPlugin('rxjs', rxjs); exposeToPlugin('rxjs', rxjs);
exposeToPlugin('rxjs/operators', rxjsOperators); exposeToPlugin('rxjs/operators', rxjsOperators);
@ -120,24 +115,16 @@ exposeToPlugin('app/core/services/backend_srv', {
getBackendSrv, getBackendSrv,
}); });
exposeToPlugin('app/plugins/sdk', sdk);
exposeToPlugin('app/core/utils/datemath', grafanaData.dateMath); exposeToPlugin('app/core/utils/datemath', grafanaData.dateMath);
exposeToPlugin('app/core/utils/flatten', flatten); exposeToPlugin('app/core/utils/flatten', flatten);
exposeToPlugin('app/core/utils/kbn', kbn); exposeToPlugin('app/core/utils/kbn', kbn);
exposeToPlugin('app/core/utils/ticks', ticks); exposeToPlugin('app/core/utils/ticks', ticks);
exposeToPlugin('app/core/utils/promiseToDigest', {
promiseToDigest: promiseToDigest,
__esModule: true,
});
exposeToPlugin('app/core/config', config); exposeToPlugin('app/core/config', config);
exposeToPlugin('app/core/time_series', TimeSeries); exposeToPlugin('app/core/time_series', TimeSeries);
exposeToPlugin('app/core/time_series2', TimeSeries); exposeToPlugin('app/core/time_series2', TimeSeries);
exposeToPlugin('app/core/table_model', TableModel); exposeToPlugin('app/core/table_model', TableModel);
exposeToPlugin('app/core/app_events', appEvents); exposeToPlugin('app/core/app_events', appEvents);
exposeToPlugin('app/core/core_module', coreModule);
exposeToPlugin('app/core/core', { exposeToPlugin('app/core/core', {
coreModule: coreModule,
appEvents: appEvents, appEvents: appEvents,
contextSrv: contextSrv, contextSrv: contextSrv,
__esModule: true, __esModule: true,

@ -1,4 +1,3 @@
import angular from 'angular';
import { castArray, isEqual } from 'lodash'; import { castArray, isEqual } from 'lodash';
import { import {
DataQuery, DataQuery,
@ -601,7 +600,7 @@ const timeRangeUpdated =
const updatedVariable = getVariable<VariableWithOptions>(identifier.id, getState()); const updatedVariable = getVariable<VariableWithOptions>(identifier.id, getState());
const updatedOptions = updatedVariable.options; const updatedOptions = updatedVariable.options;
if (angular.toJson(previousOptions) !== angular.toJson(updatedOptions)) { if (JSON.stringify(previousOptions) !== JSON.stringify(updatedOptions)) {
const dashboard = getState().dashboard.getModel(); const dashboard = getState().dashboard.getModel();
dashboard?.templateVariableValueUpdated(); dashboard?.templateVariableValueUpdated();
} }

@ -1,5 +1,3 @@
import { auto } from 'angular';
export class QueryCtrl { export class QueryCtrl {
target: any; target: any;
datasource: any; datasource: any;
@ -8,7 +6,7 @@ export class QueryCtrl {
hasRawMode = false; hasRawMode = false;
error = ''; error = '';
constructor(public $scope: any, _$injector: auto.IInjectorService) { constructor(public $scope: any) {
this.panelCtrl = this.panelCtrl || { panel: {} }; this.panelCtrl = this.panelCtrl || { panel: {} };
this.target = this.target || { target: '' }; this.target = this.target || { target: '' };
this.panel = this.panelCtrl.panel; 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 './store';
export * from './ldap'; export * from './ldap';
export * from './appEvent'; export * from './appEvent';
export * from './angular';
export * from './query'; export * from './query';
export * from './preferences'; export * from './preferences';
export * from './accessControl'; export * from './accessControl';

Loading…
Cancel
Save