Commit f7c55d39 by Ryan McKinley Committed by Torkel Ödegaard

Plugins: better warning when plugins fail to load (#18671)

* better pluin feedback

* add server side check for module.js
parent c98c5c3c
...@@ -216,6 +216,17 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error { ...@@ -216,6 +216,17 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error {
} }
loader = reflect.New(reflect.TypeOf(pluginGoType)).Interface().(PluginLoader) loader = reflect.New(reflect.TypeOf(pluginGoType)).Interface().(PluginLoader)
// External plugins need a module.js file for SystemJS to load
if !strings.HasPrefix(pluginJsonFilePath, setting.StaticRootPath) {
module := filepath.Join(filepath.Dir(pluginJsonFilePath), "module.js")
if _, err := os.Stat(module); os.IsNotExist(err) {
plog.Warn("Plugin missing module.js",
"name", pluginCommon.Name,
"warning", "Missing module.js, If you loaded this plugin from git, make sure to compile it.",
"path", module)
}
}
reader.Seek(0, 0) reader.Seek(0, 0)
return loader.Load(jsonParser, currentDir) return loader.Load(jsonParser, currentDir)
} }
......
import React, { FunctionComponent } from 'react'; import React, { FunctionComponent, ReactNode } from 'react';
import { AppNotificationSeverity } from 'app/types'; import { AppNotificationSeverity } from 'app/types';
interface Props { interface Props {
title: string; title: string;
icon?: string; icon?: string;
text?: string; body?: ReactNode;
severity: AppNotificationSeverity; severity: AppNotificationSeverity;
onClose?: () => void; onClose?: () => void;
} }
...@@ -22,7 +22,7 @@ function getIconFromSeverity(severity: AppNotificationSeverity): string { ...@@ -22,7 +22,7 @@ function getIconFromSeverity(severity: AppNotificationSeverity): string {
} }
} }
export const AlertBox: FunctionComponent<Props> = ({ title, icon, text, severity, onClose }) => { export const AlertBox: FunctionComponent<Props> = ({ title, icon, body, severity, onClose }) => {
return ( return (
<div className={`alert alert-${severity}`}> <div className={`alert alert-${severity}`}>
<div className="alert-icon"> <div className="alert-icon">
...@@ -30,7 +30,7 @@ export const AlertBox: FunctionComponent<Props> = ({ title, icon, text, severity ...@@ -30,7 +30,7 @@ export const AlertBox: FunctionComponent<Props> = ({ title, icon, text, severity
</div> </div>
<div className="alert-body"> <div className="alert-body">
<div className="alert-title">{title}</div> <div className="alert-title">{title}</div>
{text && <div className="alert-text">{text}</div>} {body && <div className="alert-text">{body}</div>}
</div> </div>
{onClose && ( {onClose && (
<button type="button" className="alert-close" onClick={onClose}> <button type="button" className="alert-close" onClick={onClose}>
......
...@@ -26,7 +26,7 @@ export default class AppNotificationItem extends Component<Props> { ...@@ -26,7 +26,7 @@ export default class AppNotificationItem extends Component<Props> {
<AlertBox <AlertBox
severity={appNotification.severity} severity={appNotification.severity}
title={appNotification.title} title={appNotification.title}
text={appNotification.text} body={appNotification.text}
icon={appNotification.icon} icon={appNotification.icon}
onClose={() => onClearNotification(appNotification.id)} onClose={() => onClearNotification(appNotification.id)}
/> />
......
...@@ -245,7 +245,7 @@ export class DashboardPage extends PureComponent<Props, State> { ...@@ -245,7 +245,7 @@ export class DashboardPage extends PureComponent<Props, State> {
<AlertBox <AlertBox
severity={AppNotificationSeverity.Error} severity={AppNotificationSeverity.Error}
title={initError.message} title={initError.message}
text={getMessageFromError(initError.error)} body={getMessageFromError(initError.error)}
/> />
</div> </div>
); );
......
// Libraries // Libraries
import _ from 'lodash'; import _ from 'lodash';
import React, { PureComponent } from 'react'; import React, { PureComponent, ReactNode } from 'react';
// Components // Components
import { AlertBox } from 'app/core/components/AlertBox/AlertBox'; import { AlertBox } from 'app/core/components/AlertBox/AlertBox';
// Types // Types
import { AppNotificationSeverity } from 'app/types'; import { AppNotificationSeverity } from 'app/types';
import { PanelProps, PanelPlugin, PluginType } from '@grafana/ui'; import { PanelProps, PanelPlugin, PluginType, PanelPluginMeta } from '@grafana/ui';
interface Props { interface Props {
pluginId: string; title: string;
text?: ReactNode;
} }
class PanelPluginNotFound extends PureComponent<Props> { class PanelPluginError extends PureComponent<Props> {
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
} }
...@@ -28,16 +29,33 @@ class PanelPluginNotFound extends PureComponent<Props> { ...@@ -28,16 +29,33 @@ class PanelPluginNotFound extends PureComponent<Props> {
return ( return (
<div style={style}> <div style={style}>
<AlertBox severity={AppNotificationSeverity.Error} title={`Panel plugin not found: ${this.props.pluginId}`} /> <AlertBox severity={AppNotificationSeverity.Error} {...this.props} />
</div> </div>
); );
} }
} }
export function getPanelPluginLoadError(meta: PanelPluginMeta, err: any): PanelPlugin {
const NotFound = class NotFound extends PureComponent<PanelProps> {
render() {
const text = (
<>
Check the server startup logs for more information. <br />
If this plugin was loaded from git, make sure it was compiled.
</>
);
return <PanelPluginError title={`Error loading: ${meta.id}`} text={text} />;
}
};
const plugin = new PanelPlugin(NotFound);
plugin.meta = meta;
return plugin;
}
export function getPanelPluginNotFound(id: string): PanelPlugin { export function getPanelPluginNotFound(id: string): PanelPlugin {
const NotFound = class NotFound extends PureComponent<PanelProps> { const NotFound = class NotFound extends PureComponent<PanelProps> {
render() { render() {
return <PanelPluginNotFound pluginId={id} />; return <PanelPluginError title={`Panel plugin not found: ${id}`} />;
} }
}; };
......
...@@ -199,7 +199,7 @@ export function importAppPlugin(meta: PluginMeta): Promise<AppPlugin> { ...@@ -199,7 +199,7 @@ export function importAppPlugin(meta: PluginMeta): Promise<AppPlugin> {
}); });
} }
import { getPanelPluginNotFound } from '../dashboard/dashgrid/PanelPluginNotFound'; import { getPanelPluginNotFound, getPanelPluginLoadError } from '../dashboard/dashgrid/PanelPluginError';
interface PanelCache { interface PanelCache {
[key: string]: PanelPlugin; [key: string]: PanelPlugin;
...@@ -233,7 +233,7 @@ export function importPanelPlugin(id: string): Promise<PanelPlugin> { ...@@ -233,7 +233,7 @@ export function importPanelPlugin(id: string): Promise<PanelPlugin> {
}) })
.catch(err => { .catch(err => {
// TODO, maybe a different error plugin // TODO, maybe a different error plugin
console.log('Error loading panel plugin', err); console.warn('Error loading panel plugin: ' + id, err);
return getPanelPluginNotFound(id); return getPanelPluginLoadError(meta, err);
}); });
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment