mirror of https://github.com/grafana/grafana
Scenes: Remove old scenes stuff (#79760)
* Scenes: Remove old scenes stuff * Fixes * Fixes * updatepull/80023/head
parent
692e8958a3
commit
f0c38611a2
@ -1,57 +0,0 @@ |
||||
// Libraries
|
||||
import React from 'react'; |
||||
import { useAsync } from 'react-use'; |
||||
|
||||
import { Card, Stack } from '@grafana/ui'; |
||||
import { Page } from 'app/core/components/Page/Page'; |
||||
|
||||
// Types
|
||||
import { getGrafanaSearcher } from '../search/service'; |
||||
|
||||
import { getScenes } from './scenes'; |
||||
|
||||
export interface Props {} |
||||
|
||||
export const SceneListPage = ({}: Props) => { |
||||
const scenes = getScenes(); |
||||
const results = useAsync(() => { |
||||
return getGrafanaSearcher().starred({ starred: true }); |
||||
}, []); |
||||
|
||||
return ( |
||||
<Page navId="scenes" subTitle="Experimental new runtime and state model for dashboards"> |
||||
<Page.Contents> |
||||
<Stack direction="column" gap={1}> |
||||
<h5>Apps</h5> |
||||
<Stack direction="column" gap={0}> |
||||
<Card href={`/scenes/grafana-monitoring`}> |
||||
<Card.Heading>Grafana monitoring</Card.Heading> |
||||
</Card> |
||||
</Stack> |
||||
<h5>Test scenes</h5> |
||||
<Stack direction="column" gap={0}> |
||||
{scenes.map((scene) => ( |
||||
<Card key={scene.title} href={`/scenes/${scene.title}`}> |
||||
<Card.Heading>{scene.title}</Card.Heading> |
||||
</Card> |
||||
))} |
||||
</Stack> |
||||
{results.value && ( |
||||
<> |
||||
<h5>Starred dashboards</h5> |
||||
<Stack direction="column" gap={0}> |
||||
{results.value!.view.map((dash) => ( |
||||
<Card href={`/scenes/dashboard/${dash.uid}`} key={dash.uid}> |
||||
<Card.Heading>{dash.name}</Card.Heading> |
||||
</Card> |
||||
))} |
||||
</Stack> |
||||
</> |
||||
)} |
||||
</Stack> |
||||
</Page.Contents> |
||||
</Page> |
||||
); |
||||
}; |
||||
|
||||
export default SceneListPage; |
@ -1,33 +0,0 @@ |
||||
// Libraries
|
||||
import React, { useEffect, useState } from 'react'; |
||||
|
||||
import { getUrlSyncManager } from '@grafana/scenes'; |
||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types'; |
||||
|
||||
import { getSceneByTitle } from './scenes'; |
||||
|
||||
export interface Props extends GrafanaRouteComponentProps<{ name: string }> {} |
||||
|
||||
export const ScenePage = (props: Props) => { |
||||
const scene = getSceneByTitle(props.match.params.name); |
||||
const [isInitialized, setInitialized] = useState(false); |
||||
|
||||
useEffect(() => { |
||||
if (scene && !isInitialized) { |
||||
getUrlSyncManager().initSync(scene); |
||||
setInitialized(true); |
||||
} |
||||
}, [isInitialized, scene]); |
||||
|
||||
if (!scene) { |
||||
return <h2>Scene not found</h2>; |
||||
} |
||||
|
||||
if (!isInitialized) { |
||||
return null; |
||||
} |
||||
|
||||
return <scene.Component model={scene} />; |
||||
}; |
||||
|
||||
export default ScenePage; |
@ -1,110 +0,0 @@ |
||||
// Libraries
|
||||
import React, { useMemo, useState } from 'react'; |
||||
|
||||
import { SceneApp, SceneAppPage, SceneRouteMatch, SceneAppPageLike } from '@grafana/scenes'; |
||||
import { usePageNav } from 'app/core/components/Page/usePageNav'; |
||||
import { PluginPageContext, PluginPageContextType } from 'app/features/plugins/components/PluginPageContext'; |
||||
|
||||
import { |
||||
getOverviewScene, |
||||
getHttpHandlerListScene, |
||||
getOverviewLogsScene, |
||||
getHandlerDetailsScene, |
||||
getHandlerLogsScene, |
||||
} from './scenes'; |
||||
import { getTrafficScene } from './traffic'; |
||||
|
||||
export function GrafanaMonitoringApp() { |
||||
const appScene = useMemo( |
||||
() => |
||||
new SceneApp({ |
||||
pages: [getMainPageScene()], |
||||
}), |
||||
[] |
||||
); |
||||
|
||||
const sectionNav = usePageNav('scenes')!; |
||||
const [pluginContext] = useState<PluginPageContextType>({ sectionNav }); |
||||
|
||||
return ( |
||||
<PluginPageContext.Provider value={pluginContext}> |
||||
<appScene.Component model={appScene} /> |
||||
</PluginPageContext.Provider> |
||||
); |
||||
} |
||||
|
||||
export function getMainPageScene() { |
||||
return new SceneAppPage({ |
||||
title: 'Grafana Monitoring', |
||||
subTitle: 'A custom app with embedded scenes to monitor your Grafana server', |
||||
url: '/scenes/grafana-monitoring', |
||||
hideFromBreadcrumbs: false, |
||||
getScene: getOverviewScene, |
||||
tabs: [ |
||||
new SceneAppPage({ |
||||
title: 'Overview', |
||||
url: '/scenes/grafana-monitoring', |
||||
getScene: getOverviewScene, |
||||
preserveUrlKeys: ['from', 'to', 'var-instance'], |
||||
}), |
||||
new SceneAppPage({ |
||||
title: 'HTTP handlers', |
||||
url: '/scenes/grafana-monitoring/handlers', |
||||
getScene: getHttpHandlerListScene, |
||||
preserveUrlKeys: ['from', 'to', 'var-instance'], |
||||
drilldowns: [ |
||||
{ |
||||
routePath: '/scenes/grafana-monitoring/handlers/:handler', |
||||
getPage: getHandlerDrilldownPage, |
||||
}, |
||||
], |
||||
}), |
||||
new SceneAppPage({ |
||||
title: 'Traffic', |
||||
url: '/scenes/grafana-monitoring/traffic', |
||||
getScene: getTrafficScene, |
||||
preserveUrlKeys: ['from', 'to', 'var-instance'], |
||||
}), |
||||
new SceneAppPage({ |
||||
title: 'Logs', |
||||
url: '/scenes/grafana-monitoring/logs', |
||||
getScene: getOverviewLogsScene, |
||||
preserveUrlKeys: ['from', 'to', 'var-instance'], |
||||
}), |
||||
], |
||||
}); |
||||
} |
||||
|
||||
export function getHandlerDrilldownPage( |
||||
match: SceneRouteMatch<{ handler: string; tab?: string }>, |
||||
parent: SceneAppPageLike |
||||
) { |
||||
const handler = decodeURIComponent(match.params.handler); |
||||
const baseUrl = `/scenes/grafana-monitoring/handlers/${encodeURIComponent(handler)}`; |
||||
|
||||
return new SceneAppPage({ |
||||
title: handler, |
||||
subTitle: 'A grafana http handler is responsible for service a specific API request', |
||||
url: baseUrl, |
||||
getParentPage: () => parent, |
||||
getScene: () => getHandlerDetailsScene(handler), |
||||
tabs: [ |
||||
new SceneAppPage({ |
||||
title: 'Metrics', |
||||
url: baseUrl, |
||||
routePath: '/scenes/grafana-monitoring/handlers/:handler', |
||||
getScene: () => getHandlerDetailsScene(handler), |
||||
preserveUrlKeys: ['from', 'to', 'var-instance'], |
||||
}), |
||||
new SceneAppPage({ |
||||
title: 'Logs', |
||||
url: baseUrl + '/logs', |
||||
routePath: '/scenes/grafana-monitoring/handlers/:handler/logs', |
||||
getScene: () => getHandlerLogsScene(handler), |
||||
preserveUrlKeys: ['from', 'to', 'var-instance'], |
||||
}), |
||||
], |
||||
}); |
||||
} |
||||
|
||||
export default GrafanaMonitoringApp; |
@ -1,24 +0,0 @@ |
||||
import React from 'react'; |
||||
|
||||
import { SelectableValue } from '@grafana/data'; |
||||
import { SceneComponentProps, SceneObjectBase, SceneObjectState } from '@grafana/scenes'; |
||||
import { RadioButtonGroup } from '@grafana/ui'; |
||||
|
||||
export interface SceneRadioToggleState extends SceneObjectState { |
||||
options: Array<SelectableValue<string>>; |
||||
value: string; |
||||
onChange: (value: string) => void; |
||||
} |
||||
|
||||
export class SceneRadioToggle extends SceneObjectBase<SceneRadioToggleState> { |
||||
onChange = (value: string) => { |
||||
this.setState({ value }); |
||||
this.state.onChange(value); |
||||
}; |
||||
|
||||
static Component = ({ model }: SceneComponentProps<SceneRadioToggle>) => { |
||||
const { options, value } = model.useState(); |
||||
|
||||
return <RadioButtonGroup options={options} value={value} onChange={model.onChange} />; |
||||
}; |
||||
} |
@ -1,20 +0,0 @@ |
||||
import React from 'react'; |
||||
|
||||
import { SceneComponentProps, SceneObjectState, SceneObjectBase } from '@grafana/scenes'; |
||||
import { Input } from '@grafana/ui'; |
||||
|
||||
export interface SceneSearchBoxState extends SceneObjectState { |
||||
value: string; |
||||
} |
||||
|
||||
export class SceneSearchBox extends SceneObjectBase<SceneSearchBoxState> { |
||||
onChange = (evt: React.FormEvent<HTMLInputElement>) => { |
||||
this.setState({ value: evt.currentTarget.value }); |
||||
}; |
||||
|
||||
static Component = ({ model }: SceneComponentProps<SceneSearchBox>) => { |
||||
const { value } = model.useState(); |
||||
|
||||
return <Input width={25} placeholder="Search..." value={value} onChange={model.onChange} />; |
||||
}; |
||||
} |
@ -1,428 +0,0 @@ |
||||
import React from 'react'; |
||||
|
||||
import { FieldColorModeId, getFrameDisplayName } from '@grafana/data'; |
||||
import { locationService } from '@grafana/runtime'; |
||||
import { |
||||
SceneFlexLayout, |
||||
SceneByFrameRepeater, |
||||
SceneTimePicker, |
||||
EmbeddedScene, |
||||
SceneDataNode, |
||||
SceneTimeRange, |
||||
VariableValueSelectors, |
||||
SceneQueryRunner, |
||||
SceneControlsSpacer, |
||||
SceneDataTransformer, |
||||
SceneRefreshPicker, |
||||
SceneFlexItem, |
||||
PanelBuilders, |
||||
} from '@grafana/scenes'; |
||||
import { BigValueGraphMode, BigValueTextMode, LogsDedupStrategy, LogsSortOrder } from '@grafana/schema'; |
||||
import { LinkButton } from '@grafana/ui'; |
||||
|
||||
import { SceneRadioToggle } from './SceneRadioToggle'; |
||||
import { SceneSearchBox } from './SceneSearchBox'; |
||||
import { getTableFilterTransform, getTimeSeriesFilterTransform } from './transforms'; |
||||
import { getInstantQuery, getLinkUrlWithAppUrlState, getTimeSeriesQuery, getVariablesDefinitions } from './utils'; |
||||
|
||||
export function getHttpHandlerListScene(): EmbeddedScene { |
||||
const searchBox = new SceneSearchBox({ value: '' }); |
||||
|
||||
const httpHandlerQueries = getInstantQuery({ |
||||
expr: 'sort_desc(avg without(job, instance) (rate(grafana_http_request_duration_seconds_sum[$__rate_interval]) * 1e3)) ', |
||||
}); |
||||
|
||||
const httpHandlerQueriesFiltered = new SceneDataTransformer({ |
||||
$data: httpHandlerQueries, |
||||
transformations: [getTableFilterTransform('')], |
||||
}); |
||||
|
||||
httpHandlerQueriesFiltered.addActivationHandler(() => { |
||||
const sub = searchBox.subscribeToState((state) => { |
||||
// Update transform and re-process them
|
||||
httpHandlerQueriesFiltered.setState({ transformations: [getTableFilterTransform(state.value)] }); |
||||
httpHandlerQueriesFiltered.reprocessTransformations(); |
||||
}); |
||||
|
||||
return () => sub.unsubscribe(); |
||||
}); |
||||
|
||||
const httpHandlersTable = PanelBuilders.table() |
||||
.setTitle('Handlers') |
||||
.setData(httpHandlerQueriesFiltered) |
||||
.setOption('footer', { |
||||
enablePagination: true, |
||||
}) |
||||
.setOverrides((b) => |
||||
b |
||||
.matchFieldsWithNameByRegex('.*') |
||||
.overrideFilterable(false) |
||||
.matchFieldsWithName('Time') |
||||
.overrideCustomFieldConfig('hidden', true) |
||||
.matchFieldsWithName('Value') |
||||
.overrideDisplayName('Duration (Avg)') |
||||
.matchFieldsWithName('handler') |
||||
.overrideLinks([ |
||||
{ |
||||
title: 'Go to handler drilldown view', |
||||
url: '', |
||||
onBuildUrl: () => { |
||||
const params = locationService.getSearchObject(); |
||||
return getLinkUrlWithAppUrlState( |
||||
'/scenes/grafana-monitoring/handlers/${__value.text:percentencode}', |
||||
params |
||||
); |
||||
}, |
||||
}, |
||||
]) |
||||
) |
||||
.build(); |
||||
|
||||
const reqDurationTimeSeries = new SceneQueryRunner({ |
||||
datasource: { uid: 'gdev-prometheus' }, |
||||
queries: [ |
||||
{ |
||||
refId: 'A', |
||||
//expr: ``,
|
||||
expr: 'topk(20, avg without(job, instance) (rate(grafana_http_request_duration_seconds_sum[$__rate_interval])) * 1e3)', |
||||
range: true, |
||||
format: 'time_series', |
||||
legendFormat: '{{method}} {{handler}} (status = {{status_code}})', |
||||
maxDataPoints: 500, |
||||
}, |
||||
], |
||||
}); |
||||
|
||||
const reqDurationTimeSeriesFiltered = new SceneDataTransformer({ |
||||
$data: reqDurationTimeSeries, |
||||
transformations: [getTimeSeriesFilterTransform('')], |
||||
}); |
||||
|
||||
reqDurationTimeSeriesFiltered.addActivationHandler(() => { |
||||
const sub = searchBox.subscribeToState((state) => { |
||||
// Update transform and re-process them
|
||||
reqDurationTimeSeriesFiltered.setState({ transformations: [getTimeSeriesFilterTransform(state.value)] }); |
||||
reqDurationTimeSeriesFiltered.reprocessTransformations(); |
||||
}); |
||||
|
||||
return () => sub.unsubscribe(); |
||||
}); |
||||
|
||||
const graphsScene = new SceneByFrameRepeater({ |
||||
$data: reqDurationTimeSeriesFiltered, |
||||
body: new SceneFlexLayout({ |
||||
direction: 'column', |
||||
children: [], |
||||
}), |
||||
getLayoutChild: (data, frame, frameIndex) => { |
||||
return new SceneFlexItem({ |
||||
key: `panel-${frameIndex}`, |
||||
minHeight: 200, |
||||
$data: new SceneDataNode({ |
||||
data: { |
||||
...data, |
||||
series: [frame], |
||||
}, |
||||
}), |
||||
body: new SceneFlexLayout({ |
||||
direction: 'row', |
||||
key: `row-${frameIndex}`, |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
key: `flex1-${frameIndex}`, |
||||
body: PanelBuilders.timeseries() |
||||
.setTitle(getFrameDisplayName(frame)) |
||||
.setOption('legend', { showLegend: false }) |
||||
.setHeaderActions( |
||||
<LinkButton |
||||
fill="text" |
||||
size="sm" |
||||
icon="arrow-right" |
||||
href={getHandlerDrilldownUrl(frame.fields[1]!.labels!.handler)} |
||||
> |
||||
Details |
||||
</LinkButton> |
||||
) |
||||
.build(), |
||||
}), |
||||
|
||||
new SceneFlexItem({ |
||||
key: `flex2-${frameIndex}`, |
||||
width: 200, |
||||
body: PanelBuilders.stat() |
||||
.setTitle('Last') |
||||
.setOption('graphMode', BigValueGraphMode.None) |
||||
.setOption('textMode', BigValueTextMode.Value) |
||||
.setDisplayName('Last') |
||||
.build(), |
||||
}), |
||||
], |
||||
}), |
||||
}); |
||||
}, |
||||
}); |
||||
|
||||
const layout = new SceneFlexLayout({ |
||||
children: [new SceneFlexItem({ body: httpHandlersTable })], |
||||
}); |
||||
|
||||
const sceneToggle = new SceneRadioToggle({ |
||||
options: [ |
||||
{ value: 'table', label: 'Table' }, |
||||
{ value: 'graphs', label: 'Graphs' }, |
||||
], |
||||
value: 'table', |
||||
onChange: (value) => { |
||||
if (value === 'table') { |
||||
layout.setState({ children: [new SceneFlexItem({ body: httpHandlersTable })] }); |
||||
} else { |
||||
layout.setState({ children: [graphsScene] }); |
||||
} |
||||
}, |
||||
}); |
||||
|
||||
const scene = new EmbeddedScene({ |
||||
$variables: getVariablesDefinitions(), |
||||
$data: httpHandlerQueries, |
||||
$timeRange: new SceneTimeRange({ from: 'now-1h', to: 'now' }), |
||||
controls: [ |
||||
new VariableValueSelectors({}), |
||||
searchBox, |
||||
new SceneControlsSpacer(), |
||||
sceneToggle, |
||||
new SceneTimePicker({ isOnCanvas: true }), |
||||
new SceneRefreshPicker({ isOnCanvas: true }), |
||||
], |
||||
body: layout, |
||||
}); |
||||
|
||||
return scene; |
||||
} |
||||
|
||||
function getHandlerDrilldownUrl(handler: string) { |
||||
const params = locationService.getSearchObject(); |
||||
return getLinkUrlWithAppUrlState(`/scenes/grafana-monitoring/handlers/${encodeURIComponent(handler)}`, params); |
||||
} |
||||
|
||||
export function getHandlerDetailsScene(handler: string): EmbeddedScene { |
||||
const reqDurationTimeSeries = getTimeSeriesQuery({ |
||||
expr: `avg without(job, instance) (rate(grafana_http_request_duration_seconds_sum{handler="${handler}"}[$__rate_interval])) * 1e3`, |
||||
legendFormat: '{{method}} {{handler}} (status = {{status_code}})', |
||||
}); |
||||
|
||||
const reqCountTimeSeries = getTimeSeriesQuery({ |
||||
expr: `sum without(job, instance) (rate(grafana_http_request_duration_seconds_count{handler="${handler}"}[$__rate_interval])) `, |
||||
legendFormat: '{{method}} {{handler}} (status = {{status_code}})', |
||||
}); |
||||
|
||||
const scene = new EmbeddedScene({ |
||||
$variables: getVariablesDefinitions(), |
||||
$timeRange: new SceneTimeRange({ from: 'now-1h', to: 'now' }), |
||||
controls: [ |
||||
new VariableValueSelectors({}), |
||||
new SceneControlsSpacer(), |
||||
new SceneTimePicker({ isOnCanvas: true }), |
||||
new SceneRefreshPicker({ isOnCanvas: true }), |
||||
], |
||||
body: new SceneFlexLayout({ |
||||
direction: 'column', |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
body: PanelBuilders.timeseries().setData(reqDurationTimeSeries).setTitle('Request duration avg (ms)').build(), |
||||
}), |
||||
new SceneFlexItem({ |
||||
body: PanelBuilders.timeseries().setData(reqCountTimeSeries).setTitle('Request count/s').build(), |
||||
}), |
||||
], |
||||
}), |
||||
}); |
||||
|
||||
return scene; |
||||
} |
||||
|
||||
export function getOverviewScene(): EmbeddedScene { |
||||
const scene = new EmbeddedScene({ |
||||
$variables: getVariablesDefinitions(), |
||||
$timeRange: new SceneTimeRange({ from: 'now-1h', to: 'now' }), |
||||
controls: [ |
||||
new VariableValueSelectors({}), |
||||
new SceneControlsSpacer(), |
||||
new SceneTimePicker({ isOnCanvas: true }), |
||||
new SceneRefreshPicker({ isOnCanvas: true }), |
||||
], |
||||
body: new SceneFlexLayout({ |
||||
direction: 'column', |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
height: 150, |
||||
body: new SceneFlexLayout({ |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
body: getInstantStatPanel('grafana_stat_totals_dashboard', 'Dashboards'), |
||||
}), |
||||
new SceneFlexItem({ |
||||
body: getInstantStatPanel('grafana_stat_total_users', 'Users'), |
||||
}), |
||||
new SceneFlexItem({ |
||||
body: getInstantStatPanel('sum(grafana_stat_totals_datasource)', 'Data sources'), |
||||
}), |
||||
new SceneFlexItem({ |
||||
body: getInstantStatPanel('grafana_stat_total_service_account_tokens', 'Service account tokens'), |
||||
}), |
||||
], |
||||
}), |
||||
}), |
||||
|
||||
new SceneFlexItem({ |
||||
body: PanelBuilders.timeseries() |
||||
.setData( |
||||
new SceneQueryRunner({ |
||||
datasource: { uid: 'gdev-prometheus' }, |
||||
queries: [ |
||||
{ |
||||
refId: 'A', |
||||
expr: `sum(process_resident_memory_bytes{job="grafana", instance=~"$instance"})`, |
||||
range: true, |
||||
format: 'time_series', |
||||
maxDataPoints: 500, |
||||
}, |
||||
], |
||||
}) |
||||
) |
||||
.setTitle('Memory usage') |
||||
.setOption('legend', { showLegend: false }) |
||||
.setUnit('bytes') |
||||
.setMin(0) |
||||
.setCustomFieldConfig('lineWidth', 2) |
||||
.setCustomFieldConfig('fillOpacity', 6) |
||||
.build(), |
||||
}), |
||||
new SceneFlexItem({ |
||||
body: PanelBuilders.timeseries() |
||||
.setData( |
||||
new SceneQueryRunner({ |
||||
datasource: { uid: 'gdev-prometheus' }, |
||||
queries: [ |
||||
{ |
||||
refId: 'A', |
||||
expr: `sum(go_goroutines{job="grafana", instance=~"$instance"})`, |
||||
range: true, |
||||
format: 'time_series', |
||||
maxDataPoints: 500, |
||||
}, |
||||
], |
||||
}) |
||||
) |
||||
.setOption('legend', { showLegend: false }) |
||||
.setMin(0) |
||||
.setCustomFieldConfig('lineWidth', 2) |
||||
.setCustomFieldConfig('fillOpacity', 6) |
||||
.setTitle('Go routines') |
||||
.build(), |
||||
}), |
||||
], |
||||
}), |
||||
}); |
||||
|
||||
return scene; |
||||
} |
||||
|
||||
function getInstantStatPanel(query: string, title: string) { |
||||
return PanelBuilders.stat() |
||||
.setData(getInstantQuery({ expr: query })) |
||||
.setTitle(title) |
||||
.setColor({ fixedColor: 'text', mode: FieldColorModeId.Fixed }) |
||||
.build(); |
||||
} |
||||
|
||||
export function getHandlerLogsScene(handler: string): EmbeddedScene { |
||||
const logsQuery = new SceneQueryRunner({ |
||||
datasource: { uid: 'gdev-loki' }, |
||||
queries: [ |
||||
{ |
||||
refId: 'A', |
||||
expr: `{job="grafana"} | logfmt | handler=\`${handler}\` | __error__=\`\``, |
||||
queryType: 'range', |
||||
maxDataPoints: 5000, |
||||
}, |
||||
], |
||||
}); |
||||
|
||||
const scene = new EmbeddedScene({ |
||||
$variables: getVariablesDefinitions(), |
||||
$timeRange: new SceneTimeRange({ from: 'now-1h', to: 'now' }), |
||||
controls: [ |
||||
new VariableValueSelectors({}), |
||||
new SceneControlsSpacer(), |
||||
new SceneTimePicker({ isOnCanvas: true }), |
||||
new SceneRefreshPicker({ isOnCanvas: true }), |
||||
], |
||||
body: new SceneFlexLayout({ |
||||
direction: 'column', |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
body: PanelBuilders.logs() |
||||
.setData(logsQuery) |
||||
.setTitle('') |
||||
.setOption('showTime', true) |
||||
.setOption('showLabels', false) |
||||
.setOption('showCommonLabels', false) |
||||
.setOption('wrapLogMessage', true) |
||||
.setOption('prettifyLogMessage', false) |
||||
.setOption('enableLogDetails', true) |
||||
.setOption('dedupStrategy', LogsDedupStrategy.none) |
||||
.setOption('sortOrder', LogsSortOrder.Descending) |
||||
.build(), |
||||
}), |
||||
], |
||||
}), |
||||
}); |
||||
|
||||
return scene; |
||||
} |
||||
|
||||
export function getOverviewLogsScene(): EmbeddedScene { |
||||
const logsQuery = new SceneQueryRunner({ |
||||
datasource: { uid: 'gdev-loki' }, |
||||
queries: [ |
||||
{ |
||||
refId: 'A', |
||||
expr: `{job="grafana"} | logfmt | __error__=\`\``, |
||||
queryType: 'range', |
||||
maxDataPoints: 5000, |
||||
}, |
||||
], |
||||
}); |
||||
|
||||
const scene = new EmbeddedScene({ |
||||
$variables: getVariablesDefinitions(), |
||||
$timeRange: new SceneTimeRange({ from: 'now-1h', to: 'now' }), |
||||
controls: [ |
||||
new VariableValueSelectors({}), |
||||
new SceneControlsSpacer(), |
||||
new SceneTimePicker({ isOnCanvas: true }), |
||||
new SceneRefreshPicker({ isOnCanvas: true }), |
||||
], |
||||
body: new SceneFlexLayout({ |
||||
direction: 'column', |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
body: PanelBuilders.logs() |
||||
.setTitle('') |
||||
.setData(logsQuery) |
||||
.setOption('showTime', true) |
||||
.setOption('showLabels', false) |
||||
.setOption('showCommonLabels', false) |
||||
.setOption('wrapLogMessage', true) |
||||
.setOption('prettifyLogMessage', false) |
||||
.setOption('enableLogDetails', true) |
||||
.setOption('dedupStrategy', LogsDedupStrategy.none) |
||||
.setOption('sortOrder', LogsSortOrder.Descending) |
||||
.build(), |
||||
}), |
||||
], |
||||
}), |
||||
}); |
||||
|
||||
return scene; |
||||
} |
@ -1,127 +0,0 @@ |
||||
import React from 'react'; |
||||
|
||||
import { |
||||
SceneFlexLayout, |
||||
SceneTimePicker, |
||||
EmbeddedScene, |
||||
SceneTimeRange, |
||||
VariableValueSelectors, |
||||
SceneControlsSpacer, |
||||
SceneRefreshPicker, |
||||
SceneFlexItem, |
||||
SceneObjectState, |
||||
SceneObjectBase, |
||||
SceneObjectUrlSyncConfig, |
||||
SceneObjectUrlValues, |
||||
PanelBuilders, |
||||
} from '@grafana/scenes'; |
||||
import { Button } from '@grafana/ui'; |
||||
|
||||
import { getInstantQuery, getTimeSeriesQuery, getVariablesDefinitions } from './utils'; |
||||
|
||||
export function getTrafficScene(): EmbeddedScene { |
||||
const httpHandlersTable = PanelBuilders.table() |
||||
.setData( |
||||
getInstantQuery({ |
||||
expr: 'sort_desc(avg without(job, instance) (rate(grafana_http_request_duration_seconds_sum[$__rate_interval]) * 1e3)) ', |
||||
}) |
||||
) |
||||
.setTitle('Handlers') |
||||
.setOption('footer', { enablePagination: true }) |
||||
.setOverrides((b) => |
||||
b |
||||
.matchFieldsWithNameByRegex('.*') |
||||
.overrideFilterable(false) |
||||
.matchFieldsWithName('Time') |
||||
.overrideCustomFieldConfig('hidden', true) |
||||
.matchFieldsWithName('Value') |
||||
.overrideDisplayName('Duration (Avg)') |
||||
.matchFieldsWithName('handler') |
||||
.overrideLinks([ |
||||
{ |
||||
title: 'Go to handler drilldown view', |
||||
url: '/scenes/grafana-monitoring/traffic?handler=${__value.text:percentencode}', |
||||
}, |
||||
]) |
||||
) |
||||
.build(); |
||||
|
||||
const scene = new EmbeddedScene({ |
||||
$variables: getVariablesDefinitions(), |
||||
$timeRange: new SceneTimeRange({ from: 'now-1h', to: 'now' }), |
||||
controls: [ |
||||
new VariableValueSelectors({}), |
||||
new SceneControlsSpacer(), |
||||
new SceneTimePicker({ isOnCanvas: true }), |
||||
new SceneRefreshPicker({ isOnCanvas: true }), |
||||
], |
||||
body: new SceneFlexLayout({ |
||||
$behaviors: [new HandlerDrilldownViewBehavior()], |
||||
children: [new SceneFlexItem({ body: httpHandlersTable })], |
||||
}), |
||||
}); |
||||
|
||||
return scene; |
||||
} |
||||
|
||||
export interface HandlerDrilldownViewBehaviorState extends SceneObjectState { |
||||
handler?: string; |
||||
} |
||||
|
||||
export class HandlerDrilldownViewBehavior extends SceneObjectBase<HandlerDrilldownViewBehaviorState> { |
||||
protected _urlSync = new SceneObjectUrlSyncConfig(this, { keys: ['handler'] }); |
||||
|
||||
constructor() { |
||||
super({}); |
||||
|
||||
this.addActivationHandler(() => { |
||||
this._subs.add(this.subscribeToState((state) => this.onHandlerChanged(state.handler))); |
||||
this.onHandlerChanged(this.state.handler); |
||||
}); |
||||
} |
||||
|
||||
private onHandlerChanged(handler: string | undefined) { |
||||
const layout = this.getLayout(); |
||||
|
||||
if (handler == null) { |
||||
layout.setState({ children: layout.state.children.slice(0, 1) }); |
||||
} else { |
||||
layout.setState({ children: [layout.state.children[0], this.getDrilldownView(handler)] }); |
||||
} |
||||
} |
||||
|
||||
private getDrilldownView(handler: string): SceneFlexItem { |
||||
return new SceneFlexItem({ |
||||
key: 'drilldown-flex', |
||||
body: PanelBuilders.timeseries() |
||||
.setData( |
||||
getTimeSeriesQuery({ |
||||
expr: `rate(grafana_http_request_duration_seconds_sum{handler="${handler}"}[$__rate_interval]) * 1e3`, |
||||
}) |
||||
) |
||||
.setTitle(`Handler: ${handler} details`) |
||||
.setHeaderActions( |
||||
<Button size="sm" variant="secondary" icon="times" onClick={() => this.setState({ handler: undefined })} /> |
||||
) |
||||
.build(), |
||||
}); |
||||
} |
||||
|
||||
getUrlState() { |
||||
return { handler: this.state.handler }; |
||||
} |
||||
|
||||
updateFromUrl(values: SceneObjectUrlValues) { |
||||
if (typeof values.handler === 'string' || values.handler === undefined) { |
||||
this.setState({ handler: values.handler }); |
||||
} |
||||
} |
||||
|
||||
private getLayout() { |
||||
if (this.parent instanceof SceneFlexLayout) { |
||||
return this.parent; |
||||
} |
||||
|
||||
throw new Error('Invalid parent'); |
||||
} |
||||
} |
@ -1,42 +0,0 @@ |
||||
import { map } from 'rxjs'; |
||||
|
||||
import { |
||||
BasicValueMatcherOptions, |
||||
CustomTransformOperator, |
||||
DataTransformerID, |
||||
getFrameDisplayName, |
||||
ValueMatcherID, |
||||
} from '@grafana/data'; |
||||
import { FilterByValueMatch, FilterByValueType } from '@grafana/data/src/transformations/transformers/filterByValue'; |
||||
import { DataTransformerConfig, MatcherConfig } from '@grafana/schema'; |
||||
|
||||
export function getTableFilterTransform(query: string): DataTransformerConfig { |
||||
const regex: MatcherConfig<BasicValueMatcherOptions<string>> = { |
||||
id: ValueMatcherID.regex, |
||||
options: { value: query }, |
||||
}; |
||||
|
||||
return { |
||||
id: DataTransformerID.filterByValue, |
||||
options: { |
||||
type: FilterByValueType.include, |
||||
match: FilterByValueMatch.all, |
||||
filters: [ |
||||
{ |
||||
fieldName: 'handler', |
||||
config: regex, |
||||
}, |
||||
], |
||||
}, |
||||
}; |
||||
} |
||||
|
||||
export function getTimeSeriesFilterTransform(query: string): CustomTransformOperator { |
||||
return () => (source) => { |
||||
return source.pipe( |
||||
map((data) => { |
||||
return data.filter((frame) => getFrameDisplayName(frame).toLowerCase().includes(query.toLowerCase())); |
||||
}) |
||||
); |
||||
}; |
||||
} |
@ -1,57 +0,0 @@ |
||||
import { useLocation } from 'react-router-dom'; |
||||
|
||||
import { UrlQueryMap, urlUtil } from '@grafana/data'; |
||||
import { locationSearchToObject } from '@grafana/runtime'; |
||||
import { QueryVariable, SceneQueryRunner, SceneVariableSet } from '@grafana/scenes'; |
||||
import { PromQuery } from 'app/plugins/datasource/prometheus/types'; |
||||
|
||||
export function useAppQueryParams() { |
||||
const location = useLocation(); |
||||
return locationSearchToObject(location.search || ''); |
||||
} |
||||
|
||||
export function getLinkUrlWithAppUrlState(path: string, params: UrlQueryMap): string { |
||||
return urlUtil.renderUrl(path, params); |
||||
} |
||||
|
||||
export function getInstantQuery(query: Partial<PromQuery>): SceneQueryRunner { |
||||
return new SceneQueryRunner({ |
||||
datasource: { uid: 'gdev-prometheus' }, |
||||
queries: [ |
||||
{ |
||||
refId: 'A', |
||||
instant: true, |
||||
format: 'table', |
||||
maxDataPoints: 500, |
||||
...query, |
||||
}, |
||||
], |
||||
}); |
||||
} |
||||
|
||||
export function getTimeSeriesQuery(query: Partial<PromQuery>): SceneQueryRunner { |
||||
return new SceneQueryRunner({ |
||||
datasource: { uid: 'gdev-prometheus' }, |
||||
queries: [ |
||||
{ |
||||
refId: 'A', |
||||
range: true, |
||||
format: 'time_series', |
||||
maxDataPoints: 500, |
||||
...query, |
||||
}, |
||||
], |
||||
}); |
||||
} |
||||
|
||||
export function getVariablesDefinitions() { |
||||
return new SceneVariableSet({ |
||||
variables: [ |
||||
new QueryVariable({ |
||||
name: 'instance', |
||||
datasource: { uid: 'gdev-prometheus' }, |
||||
query: { query: 'label_values(grafana_http_request_duration_seconds_sum, instance)', refId: 'A' }, |
||||
}), |
||||
], |
||||
}); |
||||
} |
@ -1,73 +0,0 @@ |
||||
import { |
||||
SceneGridRow, |
||||
SceneTimePicker, |
||||
SceneGridLayout, |
||||
SceneTimeRange, |
||||
SceneRefreshPicker, |
||||
SceneGridItem, |
||||
PanelBuilders, |
||||
} from '@grafana/scenes'; |
||||
import { TestDataQueryType } from '@grafana-plugins/grafana-testdata-datasource/dataquery.gen'; |
||||
|
||||
import { DashboardScene } from '../../dashboard-scene/scene/DashboardScene'; |
||||
|
||||
import { getQueryRunnerWithRandomWalkQuery } from './queries'; |
||||
|
||||
export function getGridWithMultipleTimeRanges(): DashboardScene { |
||||
const globalTimeRange = new SceneTimeRange(); |
||||
const row1TimeRange = new SceneTimeRange({ |
||||
from: 'now-1y', |
||||
to: 'now', |
||||
}); |
||||
|
||||
return new DashboardScene({ |
||||
title: 'Grid with rows and different queries and time ranges', |
||||
body: new SceneGridLayout({ |
||||
children: [ |
||||
new SceneGridRow({ |
||||
$timeRange: row1TimeRange, |
||||
$data: getQueryRunnerWithRandomWalkQuery({ scenarioId: TestDataQueryType.RandomWalkTable }), |
||||
title: 'Row A - has its own query, last year time range', |
||||
key: 'Row A', |
||||
isCollapsed: true, |
||||
y: 0, |
||||
children: [ |
||||
new SceneGridItem({ |
||||
x: 0, |
||||
y: 1, |
||||
width: 12, |
||||
height: 5, |
||||
isResizable: true, |
||||
isDraggable: true, |
||||
body: PanelBuilders.timeseries().setTitle('Row A Child1').build(), |
||||
}), |
||||
new SceneGridItem({ |
||||
x: 0, |
||||
y: 5, |
||||
width: 6, |
||||
height: 5, |
||||
isResizable: true, |
||||
isDraggable: true, |
||||
body: PanelBuilders.timeseries().setTitle('Row A Child2').build(), |
||||
}), |
||||
], |
||||
}), |
||||
new SceneGridItem({ |
||||
x: 0, |
||||
y: 12, |
||||
width: 6, |
||||
height: 10, |
||||
isResizable: true, |
||||
isDraggable: true, |
||||
body: PanelBuilders.timeseries() |
||||
.setTitle('Outsider, has its own query') |
||||
.setData(getQueryRunnerWithRandomWalkQuery()) |
||||
.build(), |
||||
}), |
||||
], |
||||
}), |
||||
$timeRange: globalTimeRange, |
||||
$data: getQueryRunnerWithRandomWalkQuery(), |
||||
actions: [new SceneTimePicker({}), new SceneRefreshPicker({})], |
||||
}); |
||||
} |
@ -1,117 +0,0 @@ |
||||
import { |
||||
SceneTimePicker, |
||||
SceneFlexLayout, |
||||
SceneGridLayout, |
||||
SceneTimeRange, |
||||
SceneRefreshPicker, |
||||
SceneGridItem, |
||||
SceneFlexItem, |
||||
PanelBuilders, |
||||
} from '@grafana/scenes'; |
||||
|
||||
import { DashboardScene } from '../../dashboard-scene/scene/DashboardScene'; |
||||
|
||||
import { getQueryRunnerWithRandomWalkQuery } from './queries'; |
||||
|
||||
export function getMultipleGridLayoutTest(): DashboardScene { |
||||
return new DashboardScene({ |
||||
title: 'Multiple grid layouts test', |
||||
body: new SceneFlexLayout({ |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
body: new SceneGridLayout({ |
||||
children: [ |
||||
new SceneGridItem({ |
||||
x: 0, |
||||
y: 0, |
||||
width: 12, |
||||
height: 10, |
||||
isDraggable: true, |
||||
isResizable: true, |
||||
body: PanelBuilders.timeseries().setTitle('Dragabble and resizable').build(), |
||||
}), |
||||
new SceneGridItem({ |
||||
x: 12, |
||||
y: 0, |
||||
width: 12, |
||||
height: 10, |
||||
isResizable: false, |
||||
isDraggable: true, |
||||
body: PanelBuilders.timeseries().setTitle('Draggable only').build(), |
||||
}), |
||||
new SceneGridItem({ |
||||
x: 6, |
||||
y: 11, |
||||
width: 12, |
||||
height: 10, |
||||
isResizable: false, |
||||
isDraggable: true, |
||||
body: new SceneFlexLayout({ |
||||
direction: 'column', |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
ySizing: 'fill', |
||||
body: PanelBuilders.timeseries().setTitle('Fill height').build(), |
||||
}), |
||||
new SceneFlexItem({ |
||||
ySizing: 'fill', |
||||
body: PanelBuilders.timeseries().setTitle('Fill height').build(), |
||||
}), |
||||
], |
||||
}), |
||||
}), |
||||
], |
||||
}), |
||||
}), |
||||
new SceneFlexItem({ |
||||
body: new SceneGridLayout({ |
||||
children: [ |
||||
new SceneGridItem({ |
||||
x: 0, |
||||
y: 0, |
||||
width: 12, |
||||
height: 10, |
||||
isDraggable: true, |
||||
isResizable: true, |
||||
body: PanelBuilders.timeseries().setTitle('Dragabble and resizable').build(), |
||||
}), |
||||
new SceneGridItem({ |
||||
x: 12, |
||||
y: 0, |
||||
width: 12, |
||||
height: 10, |
||||
isResizable: false, |
||||
isDraggable: true, |
||||
body: PanelBuilders.timeseries().setTitle('Draggable only').build(), |
||||
}), |
||||
new SceneGridItem({ |
||||
x: 6, |
||||
y: 11, |
||||
width: 12, |
||||
height: 10, |
||||
isResizable: false, |
||||
isDraggable: true, |
||||
body: new SceneFlexLayout({ |
||||
direction: 'column', |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
ySizing: 'fill', |
||||
body: PanelBuilders.timeseries().setTitle('Fill height').build(), |
||||
}), |
||||
new SceneFlexItem({ |
||||
ySizing: 'fill', |
||||
body: PanelBuilders.timeseries().setTitle('Fill height').build(), |
||||
}), |
||||
], |
||||
}), |
||||
}), |
||||
], |
||||
}), |
||||
}), |
||||
], |
||||
}), |
||||
$timeRange: new SceneTimeRange(), |
||||
$data: getQueryRunnerWithRandomWalkQuery(), |
||||
actions: [new SceneTimePicker({}), new SceneRefreshPicker({})], |
||||
}); |
||||
} |
@ -1,105 +0,0 @@ |
||||
import { |
||||
SceneGridRow, |
||||
SceneTimePicker, |
||||
SceneGridLayout, |
||||
SceneTimeRange, |
||||
SceneRefreshPicker, |
||||
SceneGridItem, |
||||
PanelBuilders, |
||||
} from '@grafana/scenes'; |
||||
import { TestDataQueryType } from '@grafana-plugins/grafana-testdata-datasource/dataquery.gen'; |
||||
|
||||
import { DashboardScene } from '../../dashboard-scene/scene/DashboardScene'; |
||||
|
||||
import { getQueryRunnerWithRandomWalkQuery } from './queries'; |
||||
|
||||
export function getGridWithMultipleData(): DashboardScene { |
||||
return new DashboardScene({ |
||||
title: 'Grid with rows and different queries', |
||||
body: new SceneGridLayout({ |
||||
children: [ |
||||
new SceneGridRow({ |
||||
$timeRange: new SceneTimeRange(), |
||||
$data: getQueryRunnerWithRandomWalkQuery({ scenarioId: TestDataQueryType.RandomWalkTable }), |
||||
title: 'Row A - has its own query', |
||||
key: 'Row A', |
||||
isCollapsed: true, |
||||
y: 0, |
||||
children: [ |
||||
new SceneGridItem({ |
||||
x: 0, |
||||
y: 1, |
||||
width: 12, |
||||
height: 5, |
||||
isResizable: true, |
||||
isDraggable: true, |
||||
body: PanelBuilders.timeseries().setTitle('Row A Child1').build(), |
||||
}), |
||||
new SceneGridItem({ |
||||
x: 0, |
||||
y: 5, |
||||
width: 6, |
||||
height: 5, |
||||
isResizable: true, |
||||
isDraggable: true, |
||||
body: PanelBuilders.timeseries().setTitle('Row A Child2').build(), |
||||
}), |
||||
], |
||||
}), |
||||
new SceneGridRow({ |
||||
title: 'Row B - uses global query', |
||||
key: 'Row B', |
||||
isCollapsed: true, |
||||
y: 1, |
||||
children: [ |
||||
new SceneGridItem({ |
||||
x: 0, |
||||
y: 2, |
||||
width: 12, |
||||
height: 5, |
||||
isResizable: false, |
||||
isDraggable: true, |
||||
body: PanelBuilders.timeseries().setTitle('Row B Child1').build(), |
||||
}), |
||||
new SceneGridItem({ |
||||
x: 0, |
||||
y: 7, |
||||
width: 6, |
||||
height: 5, |
||||
isResizable: false, |
||||
isDraggable: true, |
||||
body: PanelBuilders.timeseries() |
||||
.setTitle('Row B Child2 with data') |
||||
.setData(getQueryRunnerWithRandomWalkQuery({ seriesCount: 10 })) |
||||
.build(), |
||||
}), |
||||
], |
||||
}), |
||||
new SceneGridItem({ |
||||
x: 0, |
||||
y: 12, |
||||
width: 6, |
||||
height: 10, |
||||
isResizable: true, |
||||
isDraggable: true, |
||||
body: PanelBuilders.timeseries() |
||||
.setTitle('Outsider, has its own query') |
||||
.setData(getQueryRunnerWithRandomWalkQuery({ seriesCount: 10 })) |
||||
.build(), |
||||
}), |
||||
new SceneGridItem({ |
||||
x: 6, |
||||
y: 12, |
||||
width: 12, |
||||
height: 10, |
||||
isResizable: true, |
||||
isDraggable: true, |
||||
body: PanelBuilders.timeseries().setTitle('Outsider, uses global query').build(), |
||||
}), |
||||
], |
||||
}), |
||||
$timeRange: new SceneTimeRange(), |
||||
$data: getQueryRunnerWithRandomWalkQuery(), |
||||
actions: [new SceneTimePicker({}), new SceneRefreshPicker({})], |
||||
}); |
||||
} |
@ -1,45 +0,0 @@ |
||||
import { DashboardScene } from '../../dashboard-scene/scene/DashboardScene'; |
||||
|
||||
import { getGridWithMultipleTimeRanges } from './gridMultiTimeRange'; |
||||
import { getMultipleGridLayoutTest } from './gridMultiple'; |
||||
import { getGridWithMultipleData } from './gridWithMultipleData'; |
||||
import { getQueryVariableDemo } from './queryVariableDemo'; |
||||
import { getRepeatingPanelsDemo, getRepeatingRowsDemo } from './repeatingPanels'; |
||||
import { getSceneWithRows } from './sceneWithRows'; |
||||
import { getTransformationsDemo } from './transformations'; |
||||
import { getVariablesDemo, getVariablesDemoWithAll } from './variablesDemo'; |
||||
|
||||
interface SceneDef { |
||||
title: string; |
||||
getScene: () => DashboardScene; |
||||
} |
||||
export function getScenes(): SceneDef[] { |
||||
return [ |
||||
{ title: 'Scene with rows', getScene: getSceneWithRows }, |
||||
{ title: 'Grid with rows and different queries', getScene: getGridWithMultipleData }, |
||||
{ title: 'Grid with rows and different queries and time ranges', getScene: getGridWithMultipleTimeRanges }, |
||||
{ title: 'Multiple grid layouts test', getScene: getMultipleGridLayoutTest }, |
||||
{ title: 'Variables', getScene: getVariablesDemo }, |
||||
{ title: 'Variables with All values', getScene: getVariablesDemoWithAll }, |
||||
{ title: 'Variables - Repeating panels', getScene: getRepeatingPanelsDemo }, |
||||
{ title: 'Variables - Repeating rows', getScene: getRepeatingRowsDemo }, |
||||
{ title: 'Query variable', getScene: getQueryVariableDemo }, |
||||
{ title: 'Transformations demo', getScene: getTransformationsDemo }, |
||||
]; |
||||
} |
||||
|
||||
const cache: Record<string, DashboardScene> = {}; |
||||
|
||||
export function getSceneByTitle(title: string) { |
||||
if (cache[title]) { |
||||
return cache[title]; |
||||
} |
||||
|
||||
const scene = getScenes().find((x) => x.title === title); |
||||
|
||||
if (scene) { |
||||
cache[title] = scene.getScene(); |
||||
} |
||||
|
||||
return cache[title]; |
||||
} |
@ -1,22 +0,0 @@ |
||||
import { QueryRunnerState, SceneQueryRunner } from '@grafana/scenes'; |
||||
import { TestData } from '@grafana-plugins/grafana-testdata-datasource/dataquery.gen'; |
||||
|
||||
export function getQueryRunnerWithRandomWalkQuery( |
||||
overrides?: Partial<TestData>, |
||||
queryRunnerOverrides?: Partial<QueryRunnerState> |
||||
) { |
||||
return new SceneQueryRunner({ |
||||
queries: [ |
||||
{ |
||||
refId: 'A', |
||||
datasource: { |
||||
uid: 'gdev-testdata', |
||||
type: 'testdata', |
||||
}, |
||||
scenarioId: 'random_walk', |
||||
...overrides, |
||||
}, |
||||
], |
||||
...queryRunnerOverrides, |
||||
}); |
||||
} |
@ -1,68 +0,0 @@ |
||||
import { VariableRefresh } from '@grafana/data'; |
||||
import { |
||||
SceneCanvasText, |
||||
SceneTimePicker, |
||||
SceneFlexLayout, |
||||
SceneTimeRange, |
||||
VariableValueSelectors, |
||||
SceneVariableSet, |
||||
CustomVariable, |
||||
DataSourceVariable, |
||||
QueryVariable, |
||||
SceneRefreshPicker, |
||||
SceneFlexItem, |
||||
} from '@grafana/scenes'; |
||||
|
||||
import { DashboardScene } from '../../dashboard-scene/scene/DashboardScene'; |
||||
|
||||
export function getQueryVariableDemo(): DashboardScene { |
||||
return new DashboardScene({ |
||||
title: 'Query variable', |
||||
$variables: new SceneVariableSet({ |
||||
variables: [ |
||||
new CustomVariable({ |
||||
name: 'metric', |
||||
query: 'job : job, instance : instance', |
||||
}), |
||||
new DataSourceVariable({ |
||||
name: 'datasource', |
||||
pluginId: 'prometheus', |
||||
}), |
||||
new QueryVariable({ |
||||
name: 'instance (using datasource variable)', |
||||
refresh: VariableRefresh.onTimeRangeChanged, |
||||
query: { query: 'label_values(go_gc_duration_seconds, ${metric})', refId: 'A' }, |
||||
datasource: { uid: '${datasource}' }, |
||||
}), |
||||
new QueryVariable({ |
||||
name: 'label values (on time range refresh)', |
||||
refresh: VariableRefresh.onTimeRangeChanged, |
||||
query: { query: 'label_values(go_gc_duration_seconds, ${metric})', refId: 'B' }, |
||||
datasource: { uid: 'gdev-prometheus', type: 'prometheus' }, |
||||
}), |
||||
new QueryVariable({ |
||||
name: 'legacy (graphite)', |
||||
refresh: VariableRefresh.onTimeRangeChanged, |
||||
query: { queryType: 'Default', target: 'stats.response.*', refId: 'C' }, |
||||
datasource: { uid: 'gdev-graphite', type: 'graphite' }, |
||||
}), |
||||
], |
||||
}), |
||||
body: new SceneFlexLayout({ |
||||
direction: 'row', |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
width: '40%', |
||||
body: new SceneCanvasText({ |
||||
text: 'metric: ${metric}', |
||||
fontSize: 20, |
||||
align: 'center', |
||||
}), |
||||
}), |
||||
], |
||||
}), |
||||
$timeRange: new SceneTimeRange(), |
||||
actions: [new SceneTimePicker({}), new SceneRefreshPicker({})], |
||||
controls: [new VariableValueSelectors({})], |
||||
}); |
||||
} |
@ -1,189 +0,0 @@ |
||||
import { |
||||
SceneTimePicker, |
||||
SceneTimeRange, |
||||
VariableValueSelectors, |
||||
SceneVariableSet, |
||||
TestVariable, |
||||
SceneRefreshPicker, |
||||
PanelBuilders, |
||||
SceneGridLayout, |
||||
SceneControlsSpacer, |
||||
SceneGridRow, |
||||
} from '@grafana/scenes'; |
||||
import { VariableRefresh } from '@grafana/schema'; |
||||
import { PanelRepeaterGridItem } from 'app/features/dashboard-scene/scene/PanelRepeaterGridItem'; |
||||
import { RowRepeaterBehavior } from 'app/features/dashboard-scene/scene/RowRepeaterBehavior'; |
||||
|
||||
import { DashboardScene } from '../../dashboard-scene/scene/DashboardScene'; |
||||
|
||||
import { getQueryRunnerWithRandomWalkQuery } from './queries'; |
||||
|
||||
/** |
||||
* Repeat panels by variable that changes with time refresh. This tries to setup a very specific scenario |
||||
* where a variable that is slow (2s) and constantly changing it's result is used to repeat panels. This |
||||
* can be used to verify that when the time range change the repeated panels with locally scoped variable value |
||||
* still wait for the top level variable to finish loading and the repeat process to complete. |
||||
*/ |
||||
export function getRepeatingPanelsDemo(): DashboardScene { |
||||
return new DashboardScene({ |
||||
title: 'Variables - Repeating panels', |
||||
$variables: new SceneVariableSet({ |
||||
variables: [ |
||||
new TestVariable({ |
||||
name: 'server', |
||||
query: 'AB', |
||||
value: 'server', |
||||
text: '', |
||||
delayMs: 2000, |
||||
isMulti: true, |
||||
includeAll: true, |
||||
refresh: VariableRefresh.onTimeRangeChanged, |
||||
optionsToReturn: [ |
||||
{ label: 'A', value: 'A' }, |
||||
{ label: 'B', value: 'B' }, |
||||
], |
||||
options: [], |
||||
$behaviors: [changeVariable], |
||||
}), |
||||
], |
||||
}), |
||||
body: new SceneGridLayout({ |
||||
isDraggable: true, |
||||
isResizable: true, |
||||
children: [ |
||||
new PanelRepeaterGridItem({ |
||||
variableName: 'server', |
||||
x: 0, |
||||
y: 0, |
||||
width: 24, |
||||
height: 8, |
||||
itemHeight: 8, |
||||
source: PanelBuilders.timeseries() |
||||
.setTitle('server = $server') |
||||
.setData(getQueryRunnerWithRandomWalkQuery({ alias: 'server = $server' })) |
||||
.build(), |
||||
}), |
||||
], |
||||
}), |
||||
$timeRange: new SceneTimeRange(), |
||||
actions: [], |
||||
controls: [ |
||||
new VariableValueSelectors({}), |
||||
new SceneControlsSpacer(), |
||||
new SceneTimePicker({}), |
||||
new SceneRefreshPicker({}), |
||||
], |
||||
}); |
||||
} |
||||
|
||||
function changeVariable(variable: TestVariable) { |
||||
const sub = variable.subscribeToState((state, old) => { |
||||
if (!state.loading && old.loading) { |
||||
if (variable.state.optionsToReturn?.length === 2) { |
||||
variable.setState({ |
||||
query: 'ABC', |
||||
optionsToReturn: [ |
||||
{ label: 'A', value: 'A' }, |
||||
{ label: 'B', value: 'B' }, |
||||
{ label: 'C', value: 'C' }, |
||||
], |
||||
}); |
||||
} else { |
||||
variable.setState({ |
||||
query: 'AB', |
||||
optionsToReturn: [ |
||||
{ label: 'A', value: 'A' }, |
||||
{ label: 'B', value: 'B' }, |
||||
], |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
return () => { |
||||
sub.unsubscribe(); |
||||
}; |
||||
} |
||||
|
||||
export function getRepeatingRowsDemo(): DashboardScene { |
||||
return new DashboardScene({ |
||||
title: 'Variables - Repeating rows', |
||||
$variables: new SceneVariableSet({ |
||||
variables: [ |
||||
new TestVariable({ |
||||
name: 'server', |
||||
query: 'AB', |
||||
value: ['A', 'B', 'C'], |
||||
text: ['A', 'B', 'C'], |
||||
delayMs: 2000, |
||||
isMulti: true, |
||||
includeAll: true, |
||||
refresh: VariableRefresh.onTimeRangeChanged, |
||||
optionsToReturn: [ |
||||
{ label: 'A', value: 'A' }, |
||||
{ label: 'B', value: 'B' }, |
||||
{ label: 'C', value: 'C' }, |
||||
], |
||||
options: [], |
||||
//$behaviors: [changeVariable],
|
||||
}), |
||||
new TestVariable({ |
||||
name: 'pod', |
||||
query: 'AB', |
||||
value: ['Mu', 'Ma', 'Mi'], |
||||
text: ['Mu', 'Ma', 'Mi'], |
||||
delayMs: 2000, |
||||
isMulti: true, |
||||
includeAll: true, |
||||
refresh: VariableRefresh.onTimeRangeChanged, |
||||
optionsToReturn: [ |
||||
{ label: 'Mu', value: 'Mu' }, |
||||
{ label: 'Ma', value: 'Ma' }, |
||||
{ label: 'Mi', value: 'Mi' }, |
||||
], |
||||
options: [], |
||||
}), |
||||
], |
||||
}), |
||||
body: new SceneGridLayout({ |
||||
isDraggable: true, |
||||
isResizable: true, |
||||
children: [ |
||||
new SceneGridRow({ |
||||
title: 'Row $server', |
||||
key: 'Row A', |
||||
isCollapsed: false, |
||||
y: 0, |
||||
x: 0, |
||||
$behaviors: [ |
||||
new RowRepeaterBehavior({ |
||||
variableName: 'server', |
||||
sources: [ |
||||
new PanelRepeaterGridItem({ |
||||
variableName: 'pod', |
||||
x: 0, |
||||
y: 0, |
||||
width: 24, |
||||
height: 5, |
||||
itemHeight: 5, |
||||
source: PanelBuilders.timeseries() |
||||
.setTitle('server = $server, pod = $pod') |
||||
.setData(getQueryRunnerWithRandomWalkQuery({ alias: 'server = $server, pod = $pod' })) |
||||
.build(), |
||||
}), |
||||
], |
||||
}), |
||||
], |
||||
}), |
||||
], |
||||
}), |
||||
$timeRange: new SceneTimeRange(), |
||||
actions: [], |
||||
controls: [ |
||||
new VariableValueSelectors({}), |
||||
new SceneControlsSpacer(), |
||||
new SceneTimePicker({}), |
||||
new SceneRefreshPicker({}), |
||||
], |
||||
}); |
||||
} |
@ -1,58 +0,0 @@ |
||||
import { |
||||
NestedScene, |
||||
SceneTimePicker, |
||||
SceneFlexLayout, |
||||
SceneTimeRange, |
||||
SceneRefreshPicker, |
||||
SceneFlexItem, |
||||
PanelBuilders, |
||||
} from '@grafana/scenes'; |
||||
|
||||
import { DashboardScene } from '../../dashboard-scene/scene/DashboardScene'; |
||||
|
||||
import { getQueryRunnerWithRandomWalkQuery } from './queries'; |
||||
|
||||
export function getSceneWithRows(): DashboardScene { |
||||
return new DashboardScene({ |
||||
title: 'Scene with rows', |
||||
body: new SceneFlexLayout({ |
||||
direction: 'column', |
||||
children: [ |
||||
new NestedScene({ |
||||
title: 'Overview', |
||||
canCollapse: true, |
||||
body: new SceneFlexLayout({ |
||||
direction: 'row', |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
body: PanelBuilders.timeseries().setTitle('Fill height').build(), |
||||
}), |
||||
|
||||
new SceneFlexItem({ |
||||
body: PanelBuilders.timeseries().setTitle('Fill height').build(), |
||||
}), |
||||
], |
||||
}), |
||||
}), |
||||
new NestedScene({ |
||||
title: 'More server details', |
||||
canCollapse: true, |
||||
body: new SceneFlexLayout({ |
||||
direction: 'row', |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
body: PanelBuilders.timeseries().setTitle('Fill height').build(), |
||||
}), |
||||
new SceneFlexItem({ |
||||
body: PanelBuilders.timeseries().setTitle('Fill height').build(), |
||||
}), |
||||
], |
||||
}), |
||||
}), |
||||
], |
||||
}), |
||||
$timeRange: new SceneTimeRange(), |
||||
$data: getQueryRunnerWithRandomWalkQuery(), |
||||
actions: [new SceneTimePicker({}), new SceneRefreshPicker({})], |
||||
}); |
||||
} |
@ -1,79 +0,0 @@ |
||||
import { |
||||
SceneTimePicker, |
||||
SceneFlexLayout, |
||||
SceneDataTransformer, |
||||
SceneTimeRange, |
||||
SceneRefreshPicker, |
||||
SceneFlexItem, |
||||
PanelBuilders, |
||||
} from '@grafana/scenes'; |
||||
|
||||
import { DashboardScene } from '../../dashboard-scene/scene/DashboardScene'; |
||||
|
||||
import { getQueryRunnerWithRandomWalkQuery } from './queries'; |
||||
|
||||
export function getTransformationsDemo(): DashboardScene { |
||||
return new DashboardScene({ |
||||
title: 'Transformations demo', |
||||
body: new SceneFlexLayout({ |
||||
direction: 'row', |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
body: new SceneFlexLayout({ |
||||
direction: 'column', |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
body: new SceneFlexLayout({ |
||||
direction: 'row', |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
body: PanelBuilders.timeseries().setTitle('Source data (global query)').build(), |
||||
}), |
||||
new SceneFlexItem({ |
||||
body: PanelBuilders.stat() |
||||
.setTitle('Transformed data') |
||||
.setData( |
||||
new SceneDataTransformer({ |
||||
transformations: [ |
||||
{ |
||||
id: 'reduce', |
||||
options: { |
||||
reducers: ['last', 'mean'], |
||||
}, |
||||
}, |
||||
], |
||||
}) |
||||
) |
||||
.build(), |
||||
}), |
||||
], |
||||
}), |
||||
}), |
||||
new SceneFlexItem({ |
||||
body: PanelBuilders.stat() |
||||
.setTitle('Query with predefined transformations') |
||||
.setData( |
||||
new SceneDataTransformer({ |
||||
$data: getQueryRunnerWithRandomWalkQuery(), |
||||
transformations: [ |
||||
{ |
||||
id: 'reduce', |
||||
options: { |
||||
reducers: ['mean'], |
||||
}, |
||||
}, |
||||
], |
||||
}) |
||||
) |
||||
.build(), |
||||
}), |
||||
], |
||||
}), |
||||
}), |
||||
], |
||||
}), |
||||
$timeRange: new SceneTimeRange(), |
||||
$data: getQueryRunnerWithRandomWalkQuery(), |
||||
actions: [new SceneTimePicker({}), new SceneRefreshPicker({})], |
||||
}); |
||||
} |
@ -1,203 +0,0 @@ |
||||
import { |
||||
SceneCanvasText, |
||||
SceneTimePicker, |
||||
SceneFlexLayout, |
||||
SceneTimeRange, |
||||
VariableValueSelectors, |
||||
SceneVariableSet, |
||||
CustomVariable, |
||||
DataSourceVariable, |
||||
TestVariable, |
||||
NestedScene, |
||||
SceneRefreshPicker, |
||||
TextBoxVariable, |
||||
SceneFlexItem, |
||||
PanelBuilders, |
||||
} from '@grafana/scenes'; |
||||
|
||||
import { DashboardScene } from '../../dashboard-scene/scene/DashboardScene'; |
||||
|
||||
import { getQueryRunnerWithRandomWalkQuery } from './queries'; |
||||
|
||||
export function getVariablesDemo(): DashboardScene { |
||||
return new DashboardScene({ |
||||
title: 'Variables', |
||||
$variables: new SceneVariableSet({ |
||||
variables: [ |
||||
new TestVariable({ |
||||
name: 'server', |
||||
query: 'A.*', |
||||
value: 'server', |
||||
text: '', |
||||
delayMs: 1000, |
||||
options: [], |
||||
}), |
||||
new TestVariable({ |
||||
name: 'pod', |
||||
query: 'A.$server.*', |
||||
value: 'pod', |
||||
delayMs: 1000, |
||||
isMulti: true, |
||||
text: '', |
||||
options: [], |
||||
}), |
||||
new TestVariable({ |
||||
name: 'handler', |
||||
query: 'A.$server.$pod.*', |
||||
value: 'handler', |
||||
delayMs: 1000, |
||||
//isMulti: true,
|
||||
text: '', |
||||
options: [], |
||||
}), |
||||
new CustomVariable({ |
||||
name: 'custom', |
||||
query: 'A : 10,B : 20', |
||||
}), |
||||
new DataSourceVariable({ |
||||
name: 'ds', |
||||
pluginId: 'testdata', |
||||
}), |
||||
new TextBoxVariable({ |
||||
name: 'textbox', |
||||
value: 'default value', |
||||
}), |
||||
], |
||||
}), |
||||
body: new SceneFlexLayout({ |
||||
direction: 'row', |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
body: new SceneFlexLayout({ |
||||
direction: 'column', |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
body: new SceneFlexLayout({ |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
body: PanelBuilders.timeseries() |
||||
.setTitle('handler: $handler') |
||||
.setData( |
||||
getQueryRunnerWithRandomWalkQuery({ |
||||
alias: 'handler: $handler', |
||||
}) |
||||
) |
||||
.build(), |
||||
}), |
||||
new SceneFlexItem({ |
||||
body: new SceneCanvasText({ |
||||
text: 'Text: ${textbox}', |
||||
fontSize: 20, |
||||
align: 'center', |
||||
}), |
||||
}), |
||||
new SceneFlexItem({ |
||||
width: '40%', |
||||
body: new SceneCanvasText({ |
||||
text: 'server: ${server} pod:${pod}', |
||||
fontSize: 20, |
||||
align: 'center', |
||||
}), |
||||
}), |
||||
], |
||||
}), |
||||
}), |
||||
new SceneFlexItem({ |
||||
body: new NestedScene({ |
||||
title: 'Collapsable inner scene', |
||||
canCollapse: true, |
||||
body: new SceneFlexLayout({ |
||||
direction: 'row', |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
body: PanelBuilders.timeseries() |
||||
.setTitle('handler: $handler') |
||||
.setData( |
||||
getQueryRunnerWithRandomWalkQuery({ |
||||
alias: 'handler: $handler', |
||||
}) |
||||
) |
||||
.build(), |
||||
}), |
||||
], |
||||
}), |
||||
}), |
||||
}), |
||||
], |
||||
}), |
||||
}), |
||||
], |
||||
}), |
||||
$timeRange: new SceneTimeRange(), |
||||
actions: [new SceneTimePicker({}), new SceneRefreshPicker({})], |
||||
controls: [new VariableValueSelectors({})], |
||||
}); |
||||
} |
||||
|
||||
export function getVariablesDemoWithAll(): DashboardScene { |
||||
return new DashboardScene({ |
||||
title: 'Variables with All values', |
||||
$variables: new SceneVariableSet({ |
||||
variables: [ |
||||
new TestVariable({ |
||||
name: 'server', |
||||
query: 'A.*', |
||||
value: 'AA', |
||||
text: 'AA', |
||||
includeAll: true, |
||||
defaultToAll: true, |
||||
delayMs: 1000, |
||||
options: [], |
||||
}), |
||||
new TestVariable({ |
||||
name: 'pod', |
||||
query: 'A.$server.*', |
||||
value: [], |
||||
delayMs: 1000, |
||||
isMulti: true, |
||||
includeAll: true, |
||||
defaultToAll: true, |
||||
text: '', |
||||
options: [], |
||||
}), |
||||
new TestVariable({ |
||||
name: 'handler', |
||||
query: 'A.$server.$pod.*', |
||||
value: [], |
||||
delayMs: 1000, |
||||
includeAll: true, |
||||
defaultToAll: false, |
||||
isMulti: true, |
||||
text: '', |
||||
options: [], |
||||
}), |
||||
], |
||||
}), |
||||
body: new SceneFlexLayout({ |
||||
direction: 'row', |
||||
children: [ |
||||
new SceneFlexItem({ |
||||
body: PanelBuilders.timeseries() |
||||
.setTitle('handler: $handler') |
||||
.setData( |
||||
getQueryRunnerWithRandomWalkQuery({ |
||||
alias: 'handler: $handler', |
||||
}) |
||||
) |
||||
.build(), |
||||
}), |
||||
new SceneFlexItem({ |
||||
width: '40%', |
||||
body: new SceneCanvasText({ |
||||
text: 'server: ${server} pod:${pod}', |
||||
fontSize: 20, |
||||
align: 'center', |
||||
}), |
||||
}), |
||||
], |
||||
}), |
||||
$timeRange: new SceneTimeRange(), |
||||
actions: [new SceneTimePicker({}), new SceneRefreshPicker({})], |
||||
controls: [new VariableValueSelectors({})], |
||||
}); |
||||
} |
Loading…
Reference in new issue