Commit 3c21a121 by Ryan McKinley Committed by Torkel Ödegaard

Plugins: Unifying alpha state & options for all plugins (#16530)

* app pages

* app pages

* workign example

* started alpha support

* remove app stuff

* show warning on alpha/beta panels

* put app back on plugin file

* fix go

* add enum for PluginType and PluginIncludeType

* Refactoring and moving settings to plugins section

fixes #16529
parent 30dcf0f6
...@@ -613,8 +613,13 @@ server_url = ...@@ -613,8 +613,13 @@ server_url =
callback_url = callback_url =
[panels] [panels]
# here for to support old env variables, can remove after a few months
enable_alpha = false enable_alpha = false
disable_sanitize_html = false disable_sanitize_html = false
[plugins]
enable_alpha = false
app_tls_skip_verify_insecure = false
[enterprise] [enterprise]
license_path = license_path =
...@@ -540,7 +540,10 @@ log_queries = ...@@ -540,7 +540,10 @@ log_queries =
;license_path = ;license_path =
[panels] [panels]
;enable_alpha = false
# If set to true Grafana will allow script tags in text panels. Not recommended as it enable XSS vulnerabilities. # If set to true Grafana will allow script tags in text panels. Not recommended as it enable XSS vulnerabilities.
;disable_sanitize_html = false ;disable_sanitize_html = false
[plugins]
;enable_alpha = false
;app_tls_skip_verify_insecure = false
...@@ -651,26 +651,29 @@ This limit will protect the server from render overloading and make sure notific ...@@ -651,26 +651,29 @@ This limit will protect the server from render overloading and make sure notific
value is `5`. value is `5`.
### evaluation_timeout_seconds ### evaluation_timeout_seconds
Default setting for alert calculation timeout. Default value is `30` Default setting for alert calculation timeout. Default value is `30`
### notification_timeout_seconds ### notification_timeout_seconds
Default setting for alert notification timeout. Default value is `30` Default setting for alert notification timeout. Default value is `30`
### max_attempts ### max_attempts
Default setting for max attempts to sending alert notifications. Default value is `3` Default setting for max attempts to sending alert notifications. Default value is `3`
## [panels] ## [panels]
### enable_alpha
Set to true if you want to test panels that are not yet ready for general usage.
### disable_sanitize_html ### disable_sanitize_html
If set to true Grafana will allow script tags in text panels. Not recommended as it enable XSS vulnerabilities. Default If set to true Grafana will allow script tags in text panels. Not recommended as it enable XSS vulnerabilities. Default
is false. This settings was introduced in Grafana v6.0. is false. This settings was introduced in Grafana v6.0.
## [plugins]
### enable_alpha
Set to true if you want to test alpha plugins that are not yet ready for general usage.
export enum PluginState {
alpha = 'alpha', // Only included it `enable_alpha` is true
beta = 'beta', // Will show a warning banner
}
export enum PluginType {
panel = 'panel',
datasource = 'datasource',
app = 'app',
}
export interface PluginMeta { export interface PluginMeta {
id: string; id: string;
name: string; name: string;
info: PluginMetaInfo; info: PluginMetaInfo;
includes: PluginInclude[];
module: string; module: string;
baseUrl: string; includes?: PluginInclude[];
baseUrl?: string;
type: PluginType;
enabled?: boolean;
state?: PluginState;
// Datasource-specific // Datasource-specific
builtIn?: boolean; builtIn?: boolean;
...@@ -24,8 +39,17 @@ interface PluginMetaQueryOptions { ...@@ -24,8 +39,17 @@ interface PluginMetaQueryOptions {
minInterval?: boolean; minInterval?: boolean;
} }
export enum PluginIncludeType {
dashboard = 'dashboard',
page = 'page',
// Only valid for apps
panel = 'panel',
datasource = 'datasource',
}
export interface PluginInclude { export interface PluginInclude {
type: string; type: PluginIncludeType;
name: string; name: string;
path: string; path: string;
} }
......
...@@ -11,7 +11,6 @@ import ( ...@@ -11,7 +11,6 @@ import (
"github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
macaron "gopkg.in/macaron.v1" macaron "gopkg.in/macaron.v1"
) )
...@@ -21,7 +20,7 @@ var pluginProxyTransport *http.Transport ...@@ -21,7 +20,7 @@ var pluginProxyTransport *http.Transport
func (hs *HTTPServer) initAppPluginRoutes(r *macaron.Macaron) { func (hs *HTTPServer) initAppPluginRoutes(r *macaron.Macaron) {
pluginProxyTransport = &http.Transport{ pluginProxyTransport = &http.Transport{
TLSClientConfig: &tls.Config{ TLSClientConfig: &tls.Config{
InsecureSkipVerify: setting.PluginAppsSkipVerifyTLS, InsecureSkipVerify: hs.Cfg.PluginsAppsSkipVerifyTLS,
Renegotiation: tls.RenegotiateFreelyAsClient, Renegotiation: tls.RenegotiateFreelyAsClient,
}, },
Proxy: http.ProxyFromEnvironment, Proxy: http.ProxyFromEnvironment,
......
...@@ -145,7 +145,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *m.ReqContext) (map[string]interf ...@@ -145,7 +145,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *m.ReqContext) (map[string]interf
panels := map[string]interface{}{} panels := map[string]interface{}{}
for _, panel := range enabledPlugins.Panels { for _, panel := range enabledPlugins.Panels {
if panel.State == plugins.PluginStateAlpha && !hs.Cfg.EnableAlphaPanels { if panel.State == plugins.PluginStateAlpha && !hs.Cfg.PluginsEnableAlpha {
continue continue
} }
...@@ -162,6 +162,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *m.ReqContext) (map[string]interf ...@@ -162,6 +162,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *m.ReqContext) (map[string]interf
"hideFromList": panel.HideFromList, "hideFromList": panel.HideFromList,
"sort": getPanelSort(panel.Id), "sort": getPanelSort(panel.Id),
"dataFormats": panel.DataFormats, "dataFormats": panel.DataFormats,
"state": panel.State,
} }
} }
......
...@@ -39,7 +39,7 @@ func (hs *HTTPServer) GetPluginList(c *m.ReqContext) Response { ...@@ -39,7 +39,7 @@ func (hs *HTTPServer) GetPluginList(c *m.ReqContext) Response {
continue continue
} }
if pluginDef.State == plugins.PluginStateAlpha && !hs.Cfg.EnableAlphaPanels { if pluginDef.State == plugins.PluginStateAlpha && !hs.Cfg.PluginsEnableAlpha {
continue continue
} }
......
...@@ -142,9 +142,6 @@ var ( ...@@ -142,9 +142,6 @@ var (
// Basic Auth // Basic Auth
BasicAuthEnabled bool BasicAuthEnabled bool
// Plugin settings
PluginAppsSkipVerifyTLS bool
// Session settings. // Session settings.
SessionOptions session.Options SessionOptions session.Options
SessionConnMaxLifetime int64 SessionConnMaxLifetime int64
...@@ -233,7 +230,8 @@ type Cfg struct { ...@@ -233,7 +230,8 @@ type Cfg struct {
MetricsEndpointEnabled bool MetricsEndpointEnabled bool
MetricsEndpointBasicAuthUsername string MetricsEndpointBasicAuthUsername string
MetricsEndpointBasicAuthPassword string MetricsEndpointBasicAuthPassword string
EnableAlphaPanels bool PluginsEnableAlpha bool
PluginsAppsSkipVerifyTLS bool
DisableSanitizeHtml bool DisableSanitizeHtml bool
EnterpriseLicensePath string EnterpriseLicensePath string
...@@ -721,9 +719,6 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error { ...@@ -721,9 +719,6 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
authBasic := iniFile.Section("auth.basic") authBasic := iniFile.Section("auth.basic")
BasicAuthEnabled = authBasic.Key("enabled").MustBool(true) BasicAuthEnabled = authBasic.Key("enabled").MustBool(true)
// global plugin settings
PluginAppsSkipVerifyTLS = iniFile.Section("plugins").Key("app_tls_skip_verify_insecure").MustBool(false)
// Rendering // Rendering
renderSec := iniFile.Section("rendering") renderSec := iniFile.Section("rendering")
cfg.RendererUrl = renderSec.Key("server_url").String() cfg.RendererUrl = renderSec.Key("server_url").String()
...@@ -771,9 +766,17 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error { ...@@ -771,9 +766,17 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
explore := iniFile.Section("explore") explore := iniFile.Section("explore")
ExploreEnabled = explore.Key("enabled").MustBool(true) ExploreEnabled = explore.Key("enabled").MustBool(true)
panels := iniFile.Section("panels") panelsSection := iniFile.Section("panels")
cfg.EnableAlphaPanels = panels.Key("enable_alpha").MustBool(false) cfg.DisableSanitizeHtml = panelsSection.Key("disable_sanitize_html").MustBool(false)
cfg.DisableSanitizeHtml = panels.Key("disable_sanitize_html").MustBool(false)
pluginsSection := iniFile.Section("plugins")
cfg.PluginsEnableAlpha = pluginsSection.Key("enable_alpha").MustBool(false)
cfg.PluginsAppsSkipVerifyTLS = iniFile.Section("plugins").Key("app_tls_skip_verify_insecure").MustBool(false)
// check old location for this option
if panelsSection.Key("enable_alpha").MustBool(false) {
cfg.PluginsEnableAlpha = true
}
cfg.readSessionConfig() cfg.readSessionConfig()
cfg.readSmtpSettings() cfg.readSmtpSettings()
......
...@@ -7,7 +7,7 @@ import { AlertBox } from 'app/core/components/AlertBox/AlertBox'; ...@@ -7,7 +7,7 @@ import { AlertBox } from 'app/core/components/AlertBox/AlertBox';
// Types // Types
import { PanelPlugin, AppNotificationSeverity } from 'app/types'; import { PanelPlugin, AppNotificationSeverity } from 'app/types';
import { PanelProps, ReactPanelPlugin } from '@grafana/ui'; import { PanelProps, ReactPanelPlugin, PluginType } from '@grafana/ui';
interface Props { interface Props {
pluginId: string; pluginId: string;
...@@ -45,6 +45,7 @@ export function getPanelPluginNotFound(id: string): PanelPlugin { ...@@ -45,6 +45,7 @@ export function getPanelPluginNotFound(id: string): PanelPlugin {
id: id, id: id,
name: id, name: id,
sort: 100, sort: 100,
type: PluginType.panel,
module: '', module: '',
baseUrl: '', baseUrl: '',
dataFormats: [], dataFormats: [],
......
...@@ -18,6 +18,7 @@ import { PanelModel } from '../state'; ...@@ -18,6 +18,7 @@ import { PanelModel } from '../state';
import { DashboardModel } from '../state'; import { DashboardModel } from '../state';
import { PanelPlugin } from 'app/types/plugins'; import { PanelPlugin } from 'app/types/plugins';
import { VizPickerSearch } from './VizPickerSearch'; import { VizPickerSearch } from './VizPickerSearch';
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
interface Props { interface Props {
panel: PanelModel; panel: PanelModel;
...@@ -238,6 +239,7 @@ export class VisualizationTab extends PureComponent<Props, State> { ...@@ -238,6 +239,7 @@ export class VisualizationTab extends PureComponent<Props, State> {
onClose={this.onCloseVizPicker} onClose={this.onCloseVizPicker}
/> />
</FadeIn> </FadeIn>
<PluginStateinfo state={plugin.state} />
{this.renderPanelOptions()} {this.renderPanelOptions()}
</> </>
</EditorTabBody> </EditorTabBody>
......
...@@ -24,6 +24,7 @@ import { getRouteParamsId } from 'app/core/selectors/location'; ...@@ -24,6 +24,7 @@ import { getRouteParamsId } from 'app/core/selectors/location';
import { NavModel, Plugin, StoreState } from 'app/types/'; import { NavModel, Plugin, StoreState } from 'app/types/';
import { DataSourceSettings } from '@grafana/ui/src/types/'; import { DataSourceSettings } from '@grafana/ui/src/types/';
import { getDataSourceLoadingNav } from '../state/navModel'; import { getDataSourceLoadingNav } from '../state/navModel';
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
export interface Props { export interface Props {
navModel: NavModel; navModel: NavModel;
...@@ -44,11 +45,6 @@ interface State { ...@@ -44,11 +45,6 @@ interface State {
testingStatus?: string; testingStatus?: string;
} }
enum DataSourceStates {
Alpha = 'alpha',
Beta = 'beta',
}
export class DataSourceSettingsPage extends PureComponent<Props, State> { export class DataSourceSettingsPage extends PureComponent<Props, State> {
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
...@@ -110,32 +106,6 @@ export class DataSourceSettingsPage extends PureComponent<Props, State> { ...@@ -110,32 +106,6 @@ export class DataSourceSettingsPage extends PureComponent<Props, State> {
return this.props.dataSource.readOnly === true; return this.props.dataSource.readOnly === true;
} }
shouldRenderInfoBox() {
const { state } = this.props.dataSourceMeta;
return state === DataSourceStates.Alpha || state === DataSourceStates.Beta;
}
getInfoText() {
const { dataSourceMeta } = this.props;
switch (dataSourceMeta.state) {
case DataSourceStates.Alpha:
return (
'This plugin is marked as being in alpha state, which means it is in early development phase and updates' +
' will include breaking changes.'
);
case DataSourceStates.Beta:
return (
'This plugin is marked as being in a beta development state. This means it is in currently in active' +
' development and could be missing important features.'
);
}
return null;
}
renderIsReadOnlyMessage() { renderIsReadOnlyMessage() {
return ( return (
<div className="grafana-info-box span8"> <div className="grafana-info-box span8">
...@@ -196,7 +166,7 @@ export class DataSourceSettingsPage extends PureComponent<Props, State> { ...@@ -196,7 +166,7 @@ export class DataSourceSettingsPage extends PureComponent<Props, State> {
<div> <div>
<form onSubmit={this.onSubmit}> <form onSubmit={this.onSubmit}>
{this.isReadOnly() && this.renderIsReadOnlyMessage()} {this.isReadOnly() && this.renderIsReadOnlyMessage()}
{this.shouldRenderInfoBox() && <div className="grafana-info-box">{this.getInfoText()}</div>} <PluginStateinfo state={dataSourceMeta.state} />
<BasicSettings <BasicSettings
dataSourceName={dataSource.name} dataSourceName={dataSource.name}
......
...@@ -11,11 +11,9 @@ exports[`Render should render alpha info text 1`] = ` ...@@ -11,11 +11,9 @@ exports[`Render should render alpha info text 1`] = `
<form <form
onSubmit={[Function]} onSubmit={[Function]}
> >
<div <PluginStateinfo
className="grafana-info-box" state="alpha"
> />
This plugin is marked as being in alpha state, which means it is in early development phase and updates will include breaking changes.
</div>
<BasicSettings <BasicSettings
dataSourceName="gdev-cloudwatch" dataSourceName="gdev-cloudwatch"
isDefault={false} isDefault={false}
...@@ -49,6 +47,7 @@ exports[`Render should render alpha info text 1`] = ` ...@@ -49,6 +47,7 @@ exports[`Render should render alpha info text 1`] = `
} }
dataSourceMeta={ dataSourceMeta={
Object { Object {
"baseUrl": "path/to/plugin",
"defaultNavUrl": "some/url", "defaultNavUrl": "some/url",
"enabled": false, "enabled": false,
"hasUpdate": false, "hasUpdate": false,
...@@ -78,11 +77,11 @@ exports[`Render should render alpha info text 1`] = ` ...@@ -78,11 +77,11 @@ exports[`Render should render alpha info text 1`] = `
"version": "1", "version": "1",
}, },
"latestVersion": "1", "latestVersion": "1",
"module": Object {}, "module": "path/to/module",
"name": "pretty cool plugin 1", "name": "pretty cool plugin 1",
"pinned": false, "pinned": false,
"state": "alpha", "state": "alpha",
"type": "", "type": "panel",
} }
} }
onModelChange={[Function]} onModelChange={[Function]}
...@@ -113,11 +112,9 @@ exports[`Render should render beta info text 1`] = ` ...@@ -113,11 +112,9 @@ exports[`Render should render beta info text 1`] = `
<form <form
onSubmit={[Function]} onSubmit={[Function]}
> >
<div <PluginStateinfo
className="grafana-info-box" state="beta"
> />
This plugin is marked as being in a beta development state. This means it is in currently in active development and could be missing important features.
</div>
<BasicSettings <BasicSettings
dataSourceName="gdev-cloudwatch" dataSourceName="gdev-cloudwatch"
isDefault={false} isDefault={false}
...@@ -151,6 +148,7 @@ exports[`Render should render beta info text 1`] = ` ...@@ -151,6 +148,7 @@ exports[`Render should render beta info text 1`] = `
} }
dataSourceMeta={ dataSourceMeta={
Object { Object {
"baseUrl": "path/to/plugin",
"defaultNavUrl": "some/url", "defaultNavUrl": "some/url",
"enabled": false, "enabled": false,
"hasUpdate": false, "hasUpdate": false,
...@@ -180,11 +178,11 @@ exports[`Render should render beta info text 1`] = ` ...@@ -180,11 +178,11 @@ exports[`Render should render beta info text 1`] = `
"version": "1", "version": "1",
}, },
"latestVersion": "1", "latestVersion": "1",
"module": Object {}, "module": "path/to/module",
"name": "pretty cool plugin 1", "name": "pretty cool plugin 1",
"pinned": false, "pinned": false,
"state": "beta", "state": "beta",
"type": "", "type": "panel",
} }
} }
onModelChange={[Function]} onModelChange={[Function]}
...@@ -215,6 +213,7 @@ exports[`Render should render component 1`] = ` ...@@ -215,6 +213,7 @@ exports[`Render should render component 1`] = `
<form <form
onSubmit={[Function]} onSubmit={[Function]}
> >
<PluginStateinfo />
<BasicSettings <BasicSettings
dataSourceName="gdev-cloudwatch" dataSourceName="gdev-cloudwatch"
isDefault={false} isDefault={false}
...@@ -248,6 +247,7 @@ exports[`Render should render component 1`] = ` ...@@ -248,6 +247,7 @@ exports[`Render should render component 1`] = `
} }
dataSourceMeta={ dataSourceMeta={
Object { Object {
"baseUrl": "path/to/plugin",
"defaultNavUrl": "some/url", "defaultNavUrl": "some/url",
"enabled": false, "enabled": false,
"hasUpdate": false, "hasUpdate": false,
...@@ -277,11 +277,10 @@ exports[`Render should render component 1`] = ` ...@@ -277,11 +277,10 @@ exports[`Render should render component 1`] = `
"version": "1", "version": "1",
}, },
"latestVersion": "1", "latestVersion": "1",
"module": Object {}, "module": "path/to/module",
"name": "pretty cool plugin 1", "name": "pretty cool plugin 1",
"pinned": false, "pinned": false,
"state": "", "type": "panel",
"type": "",
} }
} }
onModelChange={[Function]} onModelChange={[Function]}
...@@ -317,6 +316,7 @@ exports[`Render should render is ready only message 1`] = ` ...@@ -317,6 +316,7 @@ exports[`Render should render is ready only message 1`] = `
> >
This datasource was added by config and cannot be modified using the UI. Please contact your server admin to update this datasource. This datasource was added by config and cannot be modified using the UI. Please contact your server admin to update this datasource.
</div> </div>
<PluginStateinfo />
<BasicSettings <BasicSettings
dataSourceName="gdev-cloudwatch" dataSourceName="gdev-cloudwatch"
isDefault={false} isDefault={false}
...@@ -350,6 +350,7 @@ exports[`Render should render is ready only message 1`] = ` ...@@ -350,6 +350,7 @@ exports[`Render should render is ready only message 1`] = `
} }
dataSourceMeta={ dataSourceMeta={
Object { Object {
"baseUrl": "path/to/plugin",
"defaultNavUrl": "some/url", "defaultNavUrl": "some/url",
"enabled": false, "enabled": false,
"hasUpdate": false, "hasUpdate": false,
...@@ -379,11 +380,10 @@ exports[`Render should render is ready only message 1`] = ` ...@@ -379,11 +380,10 @@ exports[`Render should render is ready only message 1`] = `
"version": "1", "version": "1",
}, },
"latestVersion": "1", "latestVersion": "1",
"module": Object {}, "module": "path/to/module",
"name": "pretty cool plugin 1", "name": "pretty cool plugin 1",
"pinned": false, "pinned": false,
"state": "", "type": "panel",
"type": "",
} }
} }
onModelChange={[Function]} onModelChange={[Function]}
......
import { NavModel, NavModelItem } from 'app/types'; import { NavModel, NavModelItem } from 'app/types';
import { PluginMeta, DataSourceSettings } from '@grafana/ui/src/types'; import { PluginMeta, DataSourceSettings, PluginType } from '@grafana/ui/src/types';
import config from 'app/core/config'; import config from 'app/core/config';
export function buildNavModel(dataSource: DataSourceSettings, pluginMeta: PluginMeta): NavModelItem { export function buildNavModel(dataSource: DataSourceSettings, pluginMeta: PluginMeta): NavModelItem {
...@@ -67,6 +67,7 @@ export function getDataSourceLoadingNav(pageName: string): NavModel { ...@@ -67,6 +67,7 @@ export function getDataSourceLoadingNav(pageName: string): NavModel {
}, },
{ {
id: '1', id: '1',
type: PluginType.datasource,
name: '', name: '',
info: { info: {
author: { author: {
...@@ -83,7 +84,7 @@ export function getDataSourceLoadingNav(pageName: string): NavModel { ...@@ -83,7 +84,7 @@ export function getDataSourceLoadingNav(pageName: string): NavModel {
updated: '', updated: '',
version: '', version: '',
}, },
includes: [{ type: '', name: '', path: '' }], includes: [],
module: '', module: '',
baseUrl: '', baseUrl: '',
} }
......
...@@ -14,22 +14,22 @@ import { ...@@ -14,22 +14,22 @@ import {
} from './actions'; } from './actions';
import { getMockDataSources, getMockDataSource } from '../__mocks__/dataSourcesMocks'; import { getMockDataSources, getMockDataSource } from '../__mocks__/dataSourcesMocks';
import { LayoutModes } from 'app/core/components/LayoutSelector/LayoutSelector'; import { LayoutModes } from 'app/core/components/LayoutSelector/LayoutSelector';
import { DataSourcesState } from 'app/types'; import { DataSourcesState, Plugin } from 'app/types';
import { PluginMetaInfo } from '@grafana/ui'; import { PluginMetaInfo, PluginType } from '@grafana/ui';
const mockPlugin = () => ({ const mockPlugin = () =>
defaultNavUrl: 'defaultNavUrl', ({
enabled: true, defaultNavUrl: 'defaultNavUrl',
hasUpdate: true, enabled: true,
id: 'id', hasUpdate: true,
info: {} as PluginMetaInfo, id: 'id',
latestVersion: 'latestVersion', info: {} as PluginMetaInfo,
name: 'name', latestVersion: 'latestVersion',
pinned: true, name: 'name',
state: 'state', pinned: true,
type: 'type', type: PluginType.datasource,
module: {}, module: 'path/to/module',
}); } as Plugin);
describe('dataSourcesReducer', () => { describe('dataSourcesReducer', () => {
describe('when dataSourcesLoaded is dispatched', () => { describe('when dataSourcesLoaded is dispatched', () => {
......
import React, { FC } from 'react';
import { PluginState } from '@grafana/ui';
interface Props {
state?: PluginState;
}
function getPluginStateInfoText(state?: PluginState): string | null {
switch (state) {
case PluginState.alpha:
return (
'This plugin is marked as being in alpha state, which means it is in early development phase and updates' +
' will include breaking changes.'
);
case PluginState.beta:
return (
'This plugin is marked as being in a beta development state. This means it is in currently in active' +
' development and could be missing important features.'
);
}
return null;
}
const PluginStateinfo: FC<Props> = props => {
const text = getPluginStateInfoText(props.state);
if (!text) {
return null;
}
return <div className="grafana-info-box">{text}</div>;
};
export default PluginStateinfo;
import { Plugin, PanelPlugin, PanelDataFormat } from 'app/types'; import { Plugin, PanelPlugin, PanelDataFormat } from 'app/types';
import { PluginType } from '@grafana/ui';
export const getMockPlugins = (amount: number): Plugin[] => { export const getMockPlugins = (amount: number): Plugin[] => {
const plugins = []; const plugins = [];
...@@ -36,6 +37,7 @@ export const getMockPlugins = (amount: number): Plugin[] => { ...@@ -36,6 +37,7 @@ export const getMockPlugins = (amount: number): Plugin[] => {
export const getPanelPlugin = (options: Partial<PanelPlugin>): PanelPlugin => { export const getPanelPlugin = (options: Partial<PanelPlugin>): PanelPlugin => {
return { return {
id: options.id, id: options.id,
type: PluginType.panel,
name: options.id, name: options.id,
sort: options.sort || 1, sort: options.sort || 1,
dataFormats: [PanelDataFormat.TimeSeries], dataFormats: [PanelDataFormat.TimeSeries],
...@@ -81,9 +83,9 @@ export const getMockPlugin = () => { ...@@ -81,9 +83,9 @@ export const getMockPlugin = () => {
}, },
latestVersion: '1', latestVersion: '1',
name: 'pretty cool plugin 1', name: 'pretty cool plugin 1',
baseUrl: 'path/to/plugin',
pinned: false, pinned: false,
state: '', type: PluginType.panel,
type: '', module: 'path/to/module',
module: {}, } as Plugin;
};
}; };
...@@ -15,8 +15,9 @@ exports[`Render should render component 1`] = ` ...@@ -15,8 +15,9 @@ exports[`Render should render component 1`] = `
className="card-item-type" className="card-item-type"
> >
<i <i
className="icon-gf icon-gf-" className="icon-gf icon-gf-panel"
/> />
panel
</div> </div>
</div> </div>
<div <div
...@@ -63,8 +64,9 @@ exports[`Render should render has plugin section 1`] = ` ...@@ -63,8 +64,9 @@ exports[`Render should render has plugin section 1`] = `
className="card-item-type" className="card-item-type"
> >
<i <i
className="icon-gf icon-gf-" className="icon-gf icon-gf-panel"
/> />
panel
</div> </div>
<div <div
className="card-item-notice" className="card-item-notice"
......
import { AngularPanelPlugin, ReactPanelPlugin, PluginMetaInfo } from '@grafana/ui/src/types'; import { AngularPanelPlugin, ReactPanelPlugin, PluginMetaInfo, PluginMeta } from '@grafana/ui/src/types';
export interface PanelPlugin { export interface PanelPlugin extends PluginMeta {
id: string;
name: string;
hideFromList?: boolean; hideFromList?: boolean;
module: string;
baseUrl: string; baseUrl: string;
info: PluginMetaInfo; info: PluginMetaInfo;
sort: number; sort: number;
...@@ -19,18 +16,11 @@ export enum PanelDataFormat { ...@@ -19,18 +16,11 @@ export enum PanelDataFormat {
TimeSeries = 'time_series', TimeSeries = 'time_series',
} }
export interface Plugin { export interface Plugin extends PluginMeta {
defaultNavUrl: string; defaultNavUrl: string;
enabled: boolean;
hasUpdate: boolean; hasUpdate: boolean;
id: string;
info: PluginMetaInfo;
latestVersion: string; latestVersion: string;
name: string;
pinned: boolean; pinned: boolean;
state: string;
type: string;
module: any;
} }
export interface PluginDashboard { export interface PluginDashboard {
......
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