diff --git a/packages/grafana-data/src/types/live.ts b/packages/grafana-data/src/types/live.ts index 6d32a211c02..c806993f0dc 100644 --- a/packages/grafana-data/src/types/live.ts +++ b/packages/grafana-data/src/types/live.ts @@ -152,6 +152,13 @@ export interface LiveChannelAddress { path: string; } +/** + * Check if the address has a scope, namespace, and path + */ +export function isValidLiveChannelAddress(addr?: LiveChannelAddress): addr is LiveChannelAddress { + return !!(addr?.path && addr.namespace && addr.scope); +} + /** * @experimental */ diff --git a/pkg/api/api.go b/pkg/api/api.go index 7e3a1d5a209..e312a59dac2 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -56,7 +56,6 @@ func (hs *HTTPServer) registerRoutes() { r.Get("/admin/orgs", reqGrafanaAdmin, hs.Index) r.Get("/admin/orgs/edit/:id", reqGrafanaAdmin, hs.Index) r.Get("/admin/stats", reqGrafanaAdmin, hs.Index) - r.Get("/admin/live", reqGrafanaAdmin, hs.Index) r.Get("/admin/ldap", reqGrafanaAdmin, hs.Index) r.Get("/styleguide", reqSignedIn, hs.Index) diff --git a/pkg/api/index.go b/pkg/api/index.go index 7d9e0da8f9d..8227f190e30 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -287,12 +287,6 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool) ([]*dto {Text: "Stats", Id: "server-stats", Url: setting.AppSubUrl + "/admin/stats", Icon: "graph-bar"}, } - if hs.Live != nil { - adminNavLinks = append(adminNavLinks, &dtos.NavLink{ - Text: "Live", Id: "live", Url: setting.AppSubUrl + "/admin/live", Icon: "water", - }) - } - if setting.LDAPEnabled { adminNavLinks = append(adminNavLinks, &dtos.NavLink{ Text: "LDAP", Id: "ldap", Url: setting.AppSubUrl + "/admin/ldap", Icon: "book", diff --git a/public/app/features/admin/LiveAdmin.tsx b/public/app/features/admin/LiveAdmin.tsx deleted file mode 100644 index 6718cf18690..00000000000 --- a/public/app/features/admin/LiveAdmin.tsx +++ /dev/null @@ -1,202 +0,0 @@ -import React, { PureComponent } from 'react'; -import { hot } from 'react-hot-loader'; -import { connect } from 'react-redux'; -import { css } from 'emotion'; -import { StoreState } from 'app/types'; -import { getNavModel } from 'app/core/selectors/navModel'; -import Page from 'app/core/components/Page/Page'; -import { - NavModel, - SelectableValue, - FeatureState, - LiveChannelScope, - LiveChannelConfig, - LiveChannelSupport, -} from '@grafana/data'; -import { LivePanel } from './LivePanel'; -import { Select, FeatureInfoBox, Container } from '@grafana/ui'; -import { getGrafanaLiveCentrifugeSrv } from '../live/live'; - -interface Props { - navModel: NavModel; -} - -const scopes: Array> = [ - { label: 'Grafana', value: LiveChannelScope.Grafana, description: 'Core grafana live features' }, - { label: 'Data Sources', value: LiveChannelScope.DataSource, description: 'Data sources with live support' }, - { label: 'Plugins', value: LiveChannelScope.Plugin, description: 'Plugins with live support' }, -]; - -interface State { - scope: LiveChannelScope; - namespace?: string; - path?: string; - - namespaces: Array>; - paths: Array>; - support?: LiveChannelSupport; - config?: LiveChannelConfig; -} - -export class LiveAdmin extends PureComponent { - state: State = { - scope: LiveChannelScope.Grafana, - namespace: 'testdata', - path: 'random-2s-stream', - namespaces: [], - paths: [], - }; - // onTextChanged: ((event: FormEvent) => void) | undefined; - // onPublish: ((event: MouseEvent) => void) | undefined; - - async componentDidMount() { - const { scope, namespace, path } = this.state; - const srv = getGrafanaLiveCentrifugeSrv(); - const namespaces = await srv.scopes[scope].listNamespaces(); - const support = namespace ? await srv.scopes[scope].getChannelSupport(namespace) : undefined; - const paths = support ? await support.getSupportedPaths() : undefined; - const config = support && path ? await support.getChannelConfig(path) : undefined; - - this.setState({ - namespaces, - support, - paths: paths - ? paths.map(p => ({ - label: p.path, - value: p.path, - description: p.description, - })) - : [], - config, - }); - } - - onScopeChanged = async (v: SelectableValue) => { - if (v.value) { - const srv = getGrafanaLiveCentrifugeSrv(); - - this.setState({ - scope: v.value, - namespace: undefined, - path: undefined, - namespaces: await srv.scopes[v.value!].listNamespaces(), - paths: [], - support: undefined, - config: undefined, - }); - } - }; - - onNamespaceChanged = async (v: SelectableValue) => { - if (v.value) { - const namespace = v.value; - const srv = getGrafanaLiveCentrifugeSrv(); - const support = await srv.scopes[this.state.scope].getChannelSupport(namespace); - - this.setState({ - namespace: v.value, - paths: support!.getSupportedPaths().map(p => ({ - label: p.path, - value: p.path, - description: p.description, - })), - path: undefined, - config: undefined, - }); - } - }; - - onPathChanged = async (v: SelectableValue) => { - if (v.value) { - const path = v.value; - const srv = getGrafanaLiveCentrifugeSrv(); - const support = await srv.scopes[this.state.scope].getChannelSupport(this.state.namespace!); - if (!support) { - this.setState({ - namespace: undefined, - paths: [], - config: undefined, - support, - }); - return; - } - - this.setState({ - path, - support, - config: support.getChannelConfig(path), - }); - } - }; - - render() { - const { navModel } = this.props; - const { scope, namespace, namespaces, path, paths, config } = this.state; - - return ( - - - - -

- This supports real-time event streams in grafana core. This feature is under heavy development. Expect - the intefaces and structures to change as this becomes more production ready. -

-
-
-
-
- -
div { - margin-right: 8px; - min-width: 150px; - } - `} - > -
-
Scope
- s.value === namespace) || namespace || ''} - onChange={this.onNamespaceChanged} - allowCustomValue={true} - backspaceRemovesValue={true} - /> -
-
-
Path
- - -
- )} -
- ); - } -} diff --git a/public/app/features/plugins/built_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts index d9a53f95129..9ef69c5f7e2 100644 --- a/public/app/features/plugins/built_in_plugins.ts +++ b/public/app/features/plugins/built_in_plugins.ts @@ -55,6 +55,7 @@ import * as pieChartPanel from 'app/plugins/panel/piechart/module'; import * as barGaugePanel from 'app/plugins/panel/bargauge/module'; import * as logsPanel from 'app/plugins/panel/logs/module'; import * as newsPanel from 'app/plugins/panel/news/module'; +import * as livePanel from 'app/plugins/panel/live/module'; import * as homeLinksPanel from 'app/plugins/panel/homelinks/module'; import * as welcomeBanner from 'app/plugins/panel/welcome/module'; @@ -90,6 +91,7 @@ const builtInPlugins: any = { 'app/plugins/panel/table/module': tablePanel, 'app/plugins/panel/table-old/module': oldTablePanel, 'app/plugins/panel/news/module': newsPanel, + 'app/plugins/panel/live/module': livePanel, 'app/plugins/panel/singlestat/module': singlestatPanel, 'app/plugins/panel/stat/module': singlestatPanel2, 'app/plugins/panel/gettingstarted/module': gettingStartedPanel, diff --git a/public/app/plugins/panel/live/LiveChannelEditor.tsx b/public/app/plugins/panel/live/LiveChannelEditor.tsx new file mode 100644 index 00000000000..be1d6d6c969 --- /dev/null +++ b/public/app/plugins/panel/live/LiveChannelEditor.tsx @@ -0,0 +1,155 @@ +import React, { PureComponent } from 'react'; +import { css } from 'emotion'; +import { Select, FeatureInfoBox, Label, stylesFactory } from '@grafana/ui'; +import { + LiveChannelScope, + LiveChannelAddress, + LiveChannelSupport, + LiveChannelConfig, + SelectableValue, + StandardEditorProps, + FeatureState, + GrafanaTheme, +} from '@grafana/data'; + +import { LivePanelOptions } from './types'; +import { getGrafanaLiveCentrifugeSrv } from 'app/features/live/live'; +import { config } from 'app/core/config'; + +type Props = StandardEditorProps; + +const scopes: Array> = [ + { label: 'Grafana', value: LiveChannelScope.Grafana, description: 'Core grafana live features' }, + { label: 'Data Sources', value: LiveChannelScope.DataSource, description: 'Data sources with live support' }, + { label: 'Plugins', value: LiveChannelScope.Plugin, description: 'Plugins with live support' }, +]; + +interface State { + namespaces: Array>; + paths: Array>; + support?: LiveChannelSupport; + config?: LiveChannelConfig; +} + +export class LiveChannelEditor extends PureComponent { + state: State = { + namespaces: [], + paths: [], + }; + + async componentDidMount() { + this.updateSelectOptions(); + } + + async componentDidUpdate(oldProps: Props) { + if (this.props.value !== oldProps.value) { + this.updateSelectOptions(); + } + } + + async updateSelectOptions() { + const { scope, namespace, path } = this.props.value; + const srv = getGrafanaLiveCentrifugeSrv(); + const namespaces = await srv.scopes[scope].listNamespaces(); + const support = namespace ? await srv.scopes[scope].getChannelSupport(namespace) : undefined; + const paths = support ? await support.getSupportedPaths() : undefined; + const config = support && path ? await support.getChannelConfig(path) : undefined; + + this.setState({ + namespaces, + support, + paths: paths + ? paths.map(p => ({ + label: p.path, + value: p.path, + description: p.description, + })) + : [], + config, + }); + } + + onScopeChanged = (v: SelectableValue) => { + if (v.value) { + this.props.onChange({ scope: v.value } as LiveChannelAddress); + } + }; + + onNamespaceChanged = (v: SelectableValue) => { + const update = { + scope: this.props.value?.scope, + } as LiveChannelAddress; + if (v.value) { + update.namespace = v.value; + } + this.props.onChange(update); + }; + + onPathChanged = (v: SelectableValue) => { + const { value, onChange } = this.props; + const update = { + scope: value.scope, + namespace: value.namespace, + } as LiveChannelAddress; + if (v.value) { + update.path = v.value; + } + onChange(update); + }; + + render() { + const { namespaces, paths } = this.state; + const { scope, namespace, path } = this.props.value; + const style = getStyles(config.theme); + + return ( + <> + +

+ This supports real-time event streams in grafana core. This feature is under heavy development. Expect the + intefaces and structures to change as this becomes more production ready. +

+
+ +
+
+ + s.value === namespace) || namespace || ''} + onChange={this.onNamespaceChanged} + allowCustomValue={true} + backspaceRemovesValue={true} + /> +
+ )} + + {scope && namespace && ( +
+ +