Refactor: consistant plugin/meta usage (#16834)

This commit is contained in:
Ryan McKinley
2019-04-30 22:36:46 -07:00
committed by GitHub
parent fe20dde5db
commit 51a98565dc
16 changed files with 162 additions and 155 deletions

View File

@@ -1,6 +1,6 @@
import { ComponentClass } from 'react';
import { TimeRange } from './time';
import { PluginMeta } from './plugin';
import { PluginMeta, GrafanaPlugin } from './plugin';
import { TableData, TimeSeries, SeriesData, LoadingState } from './data';
import { PanelData } from './panel';
@@ -8,11 +8,14 @@ export interface DataSourcePluginOptionsEditorProps<TOptions> {
options: TOptions;
onOptionsChange: (options: TOptions) => void;
}
export class DataSourcePlugin<TOptions = {}, TQuery extends DataQuery = DataQuery> {
export class DataSourcePlugin<TOptions = {}, TQuery extends DataQuery = DataQuery> extends GrafanaPlugin<
DataSourcePluginMeta
> {
DataSourceClass: DataSourceConstructor<TQuery>;
components: DataSourcePluginComponents<TOptions, TQuery>;
constructor(DataSourceClass: DataSourceConstructor<TQuery>) {
super();
this.DataSourceClass = DataSourceClass;
this.components = {};
}

View File

@@ -2,16 +2,13 @@ import { ComponentClass, ComponentType } from 'react';
import { LoadingState, SeriesData } from './data';
import { TimeRange } from './time';
import { ScopedVars, DataQueryRequest, DataQueryError, LegacyResponseData } from './datasource';
import { PluginMeta } from './plugin';
import { PluginMeta, GrafanaPlugin } from './plugin';
export type InterpolateFunction = (value: string, scopedVars?: ScopedVars, format?: string | Function) => string;
export interface PanelPluginMeta extends PluginMeta {
hideFromList?: boolean;
sort: number;
angularPlugin: AngularPanelPlugin | null;
panelPlugin: PanelPlugin | null;
hasBeenImported?: boolean;
// if length>0 the query tab will show up
// Before 6.2 this could be table and/or series, but 6.2+ supports both transparently
@@ -72,14 +69,20 @@ export type PanelTypeChangedHandler<TOptions = any> = (
prevOptions: any
) => Partial<TOptions>;
export class PanelPlugin<TOptions = any> {
export class PanelPlugin<TOptions = any> extends GrafanaPlugin<PanelPluginMeta> {
panel: ComponentType<PanelProps<TOptions>>;
editor?: ComponentClass<PanelEditorProps<TOptions>>;
defaults?: TOptions;
onPanelMigration?: PanelMigrationHandler<TOptions>;
onPanelTypeChanged?: PanelTypeChangedHandler<TOptions>;
/**
* Legacy angular ctrl. If this exists it will be used instead of the panel
*/
angularPanelCtrl?: any;
constructor(panel: ComponentType<PanelProps<TOptions>>) {
super();
this.panel = panel;
}
@@ -114,16 +117,6 @@ export class PanelPlugin<TOptions = any> {
}
}
export class AngularPanelPlugin {
components: {
PanelCtrl: any;
};
constructor(PanelCtrl: any) {
this.components = { PanelCtrl: PanelCtrl };
}
}
export interface PanelSize {
width: number;
height: number;

View File

@@ -69,16 +69,20 @@ export interface PluginMetaInfo {
version: string;
}
export class AppPlugin {
meta: PluginMeta;
export class GrafanaPlugin<T extends PluginMeta> {
// Meta is filled in by the plugin loading system
meta?: T;
// Soon this will also include common config options
}
export class AppPlugin extends GrafanaPlugin<PluginMeta> {
angular?: {
ConfigCtrl?: any;
pages: { [component: string]: any };
};
constructor(meta: PluginMeta, pluginExports: any) {
this.meta = meta;
setComponentsFromLegacyExports(pluginExports: any) {
const legacy = {
ConfigCtrl: undefined,
pages: {} as any,
@@ -89,7 +93,8 @@ export class AppPlugin {
this.angular = legacy;
}
if (meta.includes) {
const { meta } = this;
if (meta && meta.includes) {
for (const include of meta.includes) {
const { type, component } = include;
if (type === PluginIncludeType.page && component) {

View File

@@ -1,6 +1,5 @@
// Libraries
import React, { PureComponent } from 'react';
import config from 'app/core/config';
import classNames from 'classnames';
// Utils & Services
@@ -9,7 +8,6 @@ import { importPanelPlugin } from 'app/features/plugins/plugin_loader';
// Components
import { AddPanelWidget } from '../components/AddPanelWidget';
import { getPanelPluginNotFound } from './PanelPluginNotFound';
import { DashboardRow } from '../components/DashboardRow';
import { PanelChrome } from './PanelChrome';
import { PanelEditor } from '../panel_editor/PanelEditor';
@@ -17,7 +15,7 @@ import { PanelResizer } from './PanelResizer';
// Types
import { PanelModel, DashboardModel } from '../state';
import { PanelPluginMeta, AngularPanelPlugin, PanelPlugin } from '@grafana/ui/src/types/panel';
import { PanelPluginMeta, PanelPlugin } from '@grafana/ui/src/types/panel';
import { AutoSizer } from 'react-virtualized';
export interface Props {
@@ -28,7 +26,7 @@ export interface Props {
}
export interface State {
plugin: PanelPluginMeta;
plugin: PanelPlugin;
angularPanel: AngularComponent;
}
@@ -72,15 +70,12 @@ export class DashboardPanel extends PureComponent<Props, State> {
const { panel } = this.props;
// handle plugin loading & changing of plugin type
if (!this.state.plugin || this.state.plugin.id !== pluginId) {
let plugin = config.panels[pluginId] || getPanelPluginNotFound(pluginId);
if (!this.state.plugin || this.state.plugin.meta.id !== pluginId) {
const plugin = await importPanelPlugin(pluginId);
// unmount angular panel
this.cleanUpAngularPanel();
// load the actual plugin code
plugin = await this.importPanelPluginModule(plugin);
if (panel.type !== pluginId) {
panel.changePlugin(plugin);
} else {
@@ -91,27 +86,6 @@ export class DashboardPanel extends PureComponent<Props, State> {
}
}
async importPanelPluginModule(plugin: PanelPluginMeta): Promise<PanelPluginMeta> {
if (plugin.hasBeenImported) {
return plugin;
}
try {
const importedPlugin = await importPanelPlugin(plugin.module);
if (importedPlugin instanceof AngularPanelPlugin) {
plugin.angularPlugin = importedPlugin as AngularPanelPlugin;
} else if (importedPlugin instanceof PanelPlugin) {
plugin.panelPlugin = importedPlugin as PanelPlugin;
}
} catch (e) {
plugin = getPanelPluginNotFound(plugin.id);
console.log('Failed to import plugin module', e);
}
plugin.hasBeenImported = true;
return plugin;
}
componentDidMount() {
this.loadPlugin(this.props.panel.type);
}
@@ -186,7 +160,7 @@ export class DashboardPanel extends PureComponent<Props, State> {
}
// if we have not loaded plugin exports yet, wait
if (!plugin || !plugin.hasBeenImported) {
if (!plugin) {
return null;
}
@@ -209,8 +183,7 @@ export class DashboardPanel extends PureComponent<Props, State> {
onMouseLeave={this.onMouseLeave}
style={styles}
>
{plugin.panelPlugin && this.renderReactPanel()}
{plugin.angularPlugin && this.renderAngularPanel()}
{plugin.angularPanelCtrl ? this.renderAngularPanel() : this.renderReactPanel()}
</div>
)}
/>

View File

@@ -16,7 +16,7 @@ import config from 'app/core/config';
// Types
import { DashboardModel, PanelModel } from '../state';
import { PanelPluginMeta, LoadingState, PanelData } from '@grafana/ui';
import { LoadingState, PanelData, PanelPlugin } from '@grafana/ui';
import { ScopedVars } from '@grafana/ui';
import templateSrv from 'app/features/templating/template_srv';
@@ -29,7 +29,7 @@ const DEFAULT_PLUGIN_ERROR = 'Error in plugin';
export interface Props {
panel: PanelModel;
dashboard: DashboardModel;
plugin: PanelPluginMeta;
plugin: PanelPlugin;
isFullscreen: boolean;
width: number;
height: number;
@@ -209,13 +209,13 @@ export class PanelChrome extends PureComponent<Props, State> {
}
get wantsQueryExecution() {
return this.props.plugin.dataFormats.length > 0 && !this.hasPanelSnapshot;
return this.props.plugin.meta.dataFormats.length > 0 && !this.hasPanelSnapshot;
}
renderPanel(width: number, height: number): JSX.Element {
const { panel, plugin } = this.props;
const { renderCounter, data, isFirstLoad } = this.state;
const PanelComponent = plugin.panelPlugin.panel;
const PanelComponent = plugin.panel;
// This is only done to increase a counter that is used by backend
// image rendering (phantomjs/headless chrome) to know when to capture image
@@ -236,7 +236,7 @@ export class PanelChrome extends PureComponent<Props, State> {
<PanelComponent
data={data}
timeRange={data.request ? data.request.range : this.timeSrv.timeRange()}
options={panel.getOptions(plugin.panelPlugin.defaults)}
options={panel.getOptions(plugin.defaults)}
width={width - 2 * config.theme.panelPadding.horizontal}
height={height - PANEL_HEADER_HEIGHT - config.theme.panelPadding.vertical}
renderCounter={renderCounter}

View File

@@ -7,14 +7,14 @@ import { AlertBox } from 'app/core/components/AlertBox/AlertBox';
// Types
import { AppNotificationSeverity } from 'app/types';
import { PanelPluginMeta, PanelProps, PanelPlugin, PluginType } from '@grafana/ui';
import { PanelProps, PanelPlugin, PluginType } from '@grafana/ui';
interface Props {
pluginId: string;
}
class PanelPluginNotFound extends PureComponent<Props> {
constructor(props) {
constructor(props: Props) {
super(props);
}
@@ -34,14 +34,15 @@ class PanelPluginNotFound extends PureComponent<Props> {
}
}
export function getPanelPluginNotFound(id: string): PanelPluginMeta {
export function getPanelPluginNotFound(id: string): PanelPlugin {
const NotFound = class NotFound extends PureComponent<PanelProps> {
render() {
return <PanelPluginNotFound pluginId={id} />;
}
};
return {
const plugin = new PanelPlugin(NotFound);
plugin.meta = {
id: id,
name: id,
sort: 100,
@@ -63,7 +64,6 @@ export function getPanelPluginNotFound(id: string): PanelPluginMeta {
updated: '',
version: '',
},
panelPlugin: new PanelPlugin(NotFound),
angularPlugin: null,
};
return plugin;
}

View File

@@ -13,12 +13,12 @@ import { AngularComponent } from 'app/core/services/AngularLoader';
import { PanelModel } from '../state/PanelModel';
import { DashboardModel } from '../state/DashboardModel';
import { PanelPluginMeta, Tooltip } from '@grafana/ui';
import { Tooltip, PanelPlugin, PanelPluginMeta } from '@grafana/ui';
interface PanelEditorProps {
panel: PanelModel;
dashboard: DashboardModel;
plugin: PanelPluginMeta;
plugin: PanelPlugin;
angularPanel?: AngularComponent;
onTypeChanged: (newType: PanelPluginMeta) => void;
}
@@ -55,7 +55,7 @@ const getPanelEditorTab = (tabId: PanelEditorTabIds): PanelEditorTab => {
};
export class PanelEditor extends PureComponent<PanelEditorProps> {
constructor(props) {
constructor(props: PanelEditorProps) {
super(props);
}
@@ -105,7 +105,7 @@ export class PanelEditor extends PureComponent<PanelEditorProps> {
];
// handle panels that do not have queries tab
if (plugin.dataFormats.length === 0) {
if (plugin.meta.dataFormats.length === 0) {
// remove queries tab
tabs.shift();
// switch tab
@@ -114,7 +114,7 @@ export class PanelEditor extends PureComponent<PanelEditorProps> {
}
}
if (config.alertingEnabled && plugin.id === 'graph') {
if (config.alertingEnabled && plugin.meta.id === 'graph') {
tabs.push(getPanelEditorTab(PanelEditorTabIds.Alert));
}

View File

@@ -18,12 +18,12 @@ import { PanelModel } from '../state';
import { DashboardModel } from '../state';
import { VizPickerSearch } from './VizPickerSearch';
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
import { PanelPluginMeta } from '@grafana/ui';
import { PanelPlugin, PanelPluginMeta } from '@grafana/ui';
interface Props {
panel: PanelModel;
dashboard: DashboardModel;
plugin: PanelPluginMeta;
plugin: PanelPlugin;
angularPanel?: AngularComponent;
onTypeChanged: (newType: PanelPluginMeta) => void;
updateLocation: typeof updateLocation;
@@ -41,7 +41,7 @@ export class VisualizationTab extends PureComponent<Props, State> {
element: HTMLElement;
angularOptions: AngularComponent;
constructor(props) {
constructor(props: Props) {
super(props);
this.state = {
@@ -54,7 +54,7 @@ export class VisualizationTab extends PureComponent<Props, State> {
getReactPanelOptions = () => {
const { panel, plugin } = this.props;
return panel.getOptions(plugin.panelPlugin.defaults);
return panel.getOptions(plugin.defaults);
};
renderPanelOptions() {
@@ -64,12 +64,8 @@ export class VisualizationTab extends PureComponent<Props, State> {
return <div ref={element => (this.element = element)} />;
}
if (plugin.panelPlugin) {
const PanelEditor = plugin.panelPlugin.editor;
if (PanelEditor) {
return <PanelEditor options={this.getReactPanelOptions()} onOptionsChange={this.onPanelOptionsChanged} />;
}
if (plugin.editor) {
return <plugin.editor options={this.getReactPanelOptions()} onOptionsChange={this.onPanelOptionsChanged} />;
}
return <p>Visualization has no options</p>;
@@ -176,11 +172,12 @@ export class VisualizationTab extends PureComponent<Props, State> {
renderToolbar = (): JSX.Element => {
const { plugin } = this.props;
const { isVizPickerOpen, searchQuery } = this.state;
const { meta } = plugin;
if (isVizPickerOpen) {
return (
<VizPickerSearch
plugin={plugin}
plugin={meta}
searchQuery={searchQuery}
onChange={this.onSearchQueryChange}
onClose={this.onCloseVizPicker}
@@ -189,8 +186,8 @@ export class VisualizationTab extends PureComponent<Props, State> {
} else {
return (
<div className="toolbar__main" onClick={this.onOpenVizPicker}>
<img className="toolbar__main-image" src={plugin.info.logos.small} />
<div className="toolbar__main-name">{plugin.name}</div>
<img className="toolbar__main-image" src={meta.info.logos.small} />
<div className="toolbar__main-name">{meta.name}</div>
<i className="fa fa-caret-down" />
</div>
);
@@ -198,14 +195,14 @@ export class VisualizationTab extends PureComponent<Props, State> {
};
onTypeChanged = (plugin: PanelPluginMeta) => {
if (plugin.id === this.props.plugin.id) {
if (plugin.id === this.props.plugin.meta.id) {
this.setState({ isVizPickerOpen: false });
} else {
this.props.onTypeChanged(plugin);
}
};
renderHelp = () => <PluginHelp plugin={this.props.plugin} type="help" />;
renderHelp = () => <PluginHelp plugin={this.props.plugin.meta} type="help" />;
setScrollTop = (event: React.MouseEvent<HTMLElement>) => {
const target = event.target as HTMLElement;
@@ -215,6 +212,7 @@ export class VisualizationTab extends PureComponent<Props, State> {
render() {
const { plugin } = this.props;
const { isVizPickerOpen, searchQuery, scrollTop } = this.state;
const { meta } = plugin;
const pluginHelp: EditorToolbarView = {
heading: 'Help',
@@ -233,13 +231,13 @@ export class VisualizationTab extends PureComponent<Props, State> {
<>
<FadeIn in={isVizPickerOpen} duration={200} unmountOnExit={true} onExited={this.clearQuery}>
<VizTypePicker
current={plugin}
current={meta}
onTypeChanged={this.onTypeChanged}
searchQuery={searchQuery}
onClose={this.onCloseVizPicker}
/>
</FadeIn>
<PluginStateinfo state={plugin.state} />
<PluginStateinfo state={meta.state} />
{this.renderPanelOptions()}
</>
</EditorTabBody>

View File

@@ -1,6 +1,5 @@
import { PanelModel } from './PanelModel';
import { getPanelPlugin } from '../../plugins/__mocks__/pluginMocks';
import { PanelPlugin, AngularPanelPlugin } from '@grafana/ui/src/types/panel';
class TablePanelCtrl {}
@@ -31,10 +30,13 @@ describe('PanelModel', () => {
};
model = new PanelModel(modelJson);
model.pluginLoaded(
getPanelPlugin({
id: 'table',
angularPlugin: new AngularPanelPlugin(TablePanelCtrl),
})
getPanelPlugin(
{
id: 'table',
},
null, // react
TablePanelCtrl // angular
)
);
});
@@ -123,15 +125,10 @@ describe('PanelModel', () => {
describe('when changing to react panel', () => {
const onPanelTypeChanged = jest.fn();
const reactPlugin = new PanelPlugin({} as any).setPanelChangeHandler(onPanelTypeChanged as any);
const reactPlugin = getPanelPlugin({ id: 'react' }).setPanelChangeHandler(onPanelTypeChanged as any);
beforeEach(() => {
model.changePlugin(
getPanelPlugin({
id: 'react',
panelPlugin: reactPlugin,
})
);
model.changePlugin(reactPlugin);
});
it('should call react onPanelTypeChanged', () => {

View File

@@ -6,7 +6,7 @@ import { Emitter } from 'app/core/utils/emitter';
import { getNextRefIdChar } from 'app/core/utils/query';
// Types
import { PanelPluginMeta, DataQuery, Threshold, ScopedVars, DataQueryResponseData } from '@grafana/ui';
import { DataQuery, Threshold, ScopedVars, DataQueryResponseData, PanelPlugin } from '@grafana/ui';
import config from 'app/core/config';
import { PanelQueryRunner } from './PanelQueryRunner';
@@ -116,7 +116,7 @@ export class PanelModel {
cacheTimeout?: any;
cachedPluginOptions?: any;
legend?: { show: boolean };
plugin?: PanelPluginMeta;
plugin?: PanelPlugin;
private queryRunner?: PanelQueryRunner;
constructor(model: any) {
@@ -248,29 +248,25 @@ export class PanelModel {
});
}
private getPluginVersion(plugin: PanelPluginMeta): string {
return this.plugin && this.plugin.info.version ? this.plugin.info.version : config.buildInfo.version;
}
pluginLoaded(plugin: PanelPluginMeta) {
pluginLoaded(plugin: PanelPlugin) {
this.plugin = plugin;
if (plugin.panelPlugin && plugin.panelPlugin.onPanelMigration) {
const version = this.getPluginVersion(plugin);
if (plugin.panel && plugin.onPanelMigration) {
const version = getPluginVersion(plugin);
if (version !== this.pluginVersion) {
this.options = plugin.panelPlugin.onPanelMigration(this);
this.options = plugin.onPanelMigration(this);
this.pluginVersion = version;
}
}
}
changePlugin(newPlugin: PanelPluginMeta) {
const pluginId = newPlugin.id;
changePlugin(newPlugin: PanelPlugin) {
const pluginId = newPlugin.meta.id;
const oldOptions: any = this.getOptionsToRemember();
const oldPluginId = this.type;
// for angular panels we must remove all events and let angular panels do some cleanup
if (this.plugin.angularPlugin) {
if (this.plugin.angularPanelCtrl) {
this.destroy();
}
@@ -291,18 +287,14 @@ export class PanelModel {
this.plugin = newPlugin;
// Let panel plugins inspect options from previous panel and keep any that it can use
const reactPanel = newPlugin.panelPlugin;
if (newPlugin.onPanelTypeChanged) {
this.options = this.options || {};
const old = oldOptions && oldOptions.options ? oldOptions.options : {};
Object.assign(this.options, newPlugin.onPanelTypeChanged(this.options, oldPluginId, old));
}
if (reactPanel) {
if (reactPanel.onPanelTypeChanged) {
this.options = this.options || {};
const old = oldOptions && oldOptions.options ? oldOptions.options : {};
Object.assign(this.options, reactPanel.onPanelTypeChanged(this.options, oldPluginId, old));
}
if (reactPanel.onPanelMigration) {
this.pluginVersion = this.getPluginVersion(newPlugin);
}
if (newPlugin.onPanelMigration) {
this.pluginVersion = getPluginVersion(newPlugin);
}
}
@@ -341,3 +333,7 @@ export class PanelModel {
}
}
}
function getPluginVersion(plugin: PanelPlugin): string {
return plugin && plugin.meta.info.version ? plugin.meta.info.version : config.buildInfo.version;
}

View File

@@ -63,7 +63,7 @@ export class DataSourceSettingsPage extends PureComponent<Props, State> {
let importedPlugin: DataSourcePlugin;
try {
importedPlugin = await importDataSourcePlugin(dataSourceMeta.module);
importedPlugin = await importDataSourcePlugin(dataSourceMeta);
} catch (e) {
console.log('Failed to import plugin module', e);
}

View File

@@ -1,4 +1,5 @@
import { PanelPluginMeta, PluginMeta, PluginType, PanelDataFormat } from '@grafana/ui';
import { PanelPluginMeta, PluginMeta, PluginType, PanelDataFormat, PanelPlugin, PanelProps } from '@grafana/ui';
import { ComponentType } from 'enzyme';
export const getMockPlugins = (amount: number): PluginMeta[] => {
const plugins = [];
@@ -33,8 +34,14 @@ export const getMockPlugins = (amount: number): PluginMeta[] => {
return plugins;
};
export const getPanelPlugin = (options: Partial<PanelPluginMeta>): PanelPluginMeta => {
return {
export const getPanelPlugin = (
options: Partial<PanelPluginMeta>,
reactPanel?: ComponentType<PanelProps>,
angularPanel?: any
): PanelPlugin => {
const plugin = new PanelPlugin(reactPanel);
plugin.angularPanelCtrl = angularPanel;
plugin.meta = {
id: options.id,
type: PluginType.panel,
name: options.id,
@@ -57,9 +64,8 @@ export const getPanelPlugin = (options: Partial<PanelPluginMeta>): PanelPluginMe
hideFromList: options.hideFromList === true,
module: '',
baseUrl: '',
panelPlugin: options.panelPlugin,
angularPlugin: options.angularPlugin,
};
return plugin;
};
export const getMockPlugin = () => {

View File

@@ -53,7 +53,7 @@ export class DatasourceSrv {
const deferred = this.$q.defer();
importDataSourcePlugin(dsConfig.meta.module)
importDataSourcePlugin(dsConfig.meta)
.then(dsPlugin => {
// check if its in cache now
if (this.datasources[name]) {

View File

@@ -4,7 +4,7 @@ import _ from 'lodash';
import config from 'app/core/config';
import coreModule from 'app/core/core_module';
import { AngularPanelPlugin, DataSourceApi } from '@grafana/ui/src/types';
import { DataSourceApi } from '@grafana/ui/src/types';
import { importPanelPlugin, importDataSourcePlugin, importAppPlugin } from './plugin_loader';
/** @ngInject */
@@ -22,7 +22,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
});
}
function relativeTemplateUrlToAbs(templateUrl, baseUrl) {
function relativeTemplateUrlToAbs(templateUrl: string, baseUrl: string) {
if (!templateUrl) {
return undefined;
}
@@ -69,9 +69,8 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
};
const panelInfo = config.panels[scope.panel.type];
return importPanelPlugin(panelInfo.module).then(panelPlugin => {
const angularPanelPlugin = panelPlugin as AngularPanelPlugin;
const PanelCtrl = angularPanelPlugin.components.PanelCtrl;
return importPanelPlugin(panelInfo.id).then(panelPlugin => {
const PanelCtrl = panelPlugin.angularPanelCtrl;
componentInfo.Component = PanelCtrl;
if (!PanelCtrl || PanelCtrl.registered) {
@@ -118,7 +117,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
}
// Annotations
case 'annotations-query-ctrl': {
return importDataSourcePlugin(scope.ctrl.currentDatasource.meta.module).then(dsPlugin => {
return importDataSourcePlugin(scope.ctrl.currentDatasource.meta).then(dsPlugin => {
return {
baseUrl: scope.ctrl.currentDatasource.meta.baseUrl,
name: 'annotations-query-ctrl-' + scope.ctrl.currentDatasource.meta.id,
@@ -134,7 +133,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
// Datasource ConfigCtrl
case 'datasource-config-ctrl': {
const dsMeta = scope.ctrl.datasourceMeta;
return importDataSourcePlugin(dsMeta.module).then(dsPlugin => {
return importDataSourcePlugin(dsMeta).then(dsPlugin => {
scope.$watch(
'ctrl.current',
() => {

View File

@@ -18,7 +18,7 @@ import config from 'app/core/config';
import TimeSeries from 'app/core/time_series2';
import TableModel from 'app/core/table_model';
import { coreModule, appEvents, contextSrv } from 'app/core/core';
import { DataSourcePlugin, AppPlugin, PanelPlugin, AngularPanelPlugin, PluginMeta } from '@grafana/ui/src/types';
import { DataSourcePlugin, AppPlugin, PanelPlugin, PluginMeta, DataSourcePluginMeta } from '@grafana/ui/src/types';
import * as datemath from 'app/core/utils/datemath';
import * as fileExport from 'app/core/utils/file_export';
import * as flatten from 'app/core/utils/flatten';
@@ -160,15 +160,18 @@ export function importPluginModule(path: string): Promise<any> {
return System.import(path);
}
export function importDataSourcePlugin(path: string): Promise<DataSourcePlugin<any>> {
return importPluginModule(path).then(pluginExports => {
export function importDataSourcePlugin(meta: DataSourcePluginMeta): Promise<DataSourcePlugin<any>> {
return importPluginModule(meta.module).then(pluginExports => {
if (pluginExports.plugin) {
return pluginExports.plugin as DataSourcePlugin<any>;
const dsPlugin = pluginExports.plugin as DataSourcePlugin<any>;
dsPlugin.meta = meta;
return dsPlugin;
}
if (pluginExports.Datasource) {
const dsPlugin = new DataSourcePlugin(pluginExports.Datasource);
dsPlugin.setComponentsFromLegacyExports(pluginExports);
dsPlugin.meta = meta;
return dsPlugin;
}
@@ -178,18 +181,50 @@ export function importDataSourcePlugin(path: string): Promise<DataSourcePlugin<a
export function importAppPlugin(meta: PluginMeta): Promise<AppPlugin> {
return importPluginModule(meta.module).then(pluginExports => {
return new AppPlugin(meta, pluginExports);
const plugin = pluginExports.plugin ? (pluginExports.plugin as AppPlugin) : new AppPlugin();
plugin.meta = meta;
plugin.setComponentsFromLegacyExports(pluginExports);
return plugin;
});
}
export function importPanelPlugin(path: string): Promise<AngularPanelPlugin | PanelPlugin> {
return importPluginModule(path).then(pluginExports => {
if (pluginExports.plugin) {
return pluginExports.plugin as PanelPlugin;
} else {
return new AngularPanelPlugin(pluginExports.PanelCtrl);
}
});
import { getPanelPluginNotFound } from '../dashboard/dashgrid/PanelPluginNotFound';
interface PanelCache {
[key: string]: PanelPlugin;
}
const panelCache: PanelCache = {};
export function importPanelPlugin(id: string): Promise<PanelPlugin> {
const loaded = panelCache[id];
if (loaded) {
return Promise.resolve(loaded);
}
const meta = config.panels[id];
if (!meta) {
return Promise.resolve(getPanelPluginNotFound(id));
}
return importPluginModule(meta.module)
.then(pluginExports => {
if (pluginExports.plugin) {
return pluginExports.plugin as PanelPlugin;
} else if (pluginExports.PanelCtrl) {
const plugin = new PanelPlugin(null);
plugin.angularPanelCtrl = pluginExports.PanelCtrl;
return plugin;
}
throw new Error('missing export: plugin or PanelCtrl');
})
.then(plugin => {
plugin.meta = meta;
return (panelCache[meta.id] = plugin);
})
.catch(err => {
// TODO, maybe a different error plugin
console.log('Error loading panel plugin', err);
return getPanelPluginNotFound(id);
});
}
export function loadPluginCss(options) {

View File

@@ -3,9 +3,11 @@ import { importDataSourcePlugin } from './plugin_loader';
import React from 'react';
import ReactDOM from 'react-dom';
import DefaultVariableQueryEditor from '../templating/DefaultVariableQueryEditor';
import { DataSourcePluginMeta } from '@grafana/ui';
import { TemplateSrv } from '../templating/template_srv';
async function loadComponent(module) {
const dsPlugin = await importDataSourcePlugin(module);
async function loadComponent(meta: DataSourcePluginMeta) {
const dsPlugin = await importDataSourcePlugin(meta);
if (dsPlugin.components.VariableQueryEditor) {
return dsPlugin.components.VariableQueryEditor;
} else {
@@ -14,11 +16,11 @@ async function loadComponent(module) {
}
/** @ngInject */
function variableQueryEditorLoader(templateSrv) {
function variableQueryEditorLoader(templateSrv: TemplateSrv) {
return {
restrict: 'E',
link: async (scope, elem) => {
const Component = await loadComponent(scope.currentDatasource.meta.module);
const Component = await loadComponent(scope.currentDatasource.meta);
const props = {
datasource: scope.currentDatasource,
query: scope.current.query,