Migrate plugins from Grafana 9.3.x to 9.4.x
Follow these instructions to migrate from Grafana 9.3.x to 9.4.x.
New navigation layout is supportedβ
First, enable the topnav
feature flag in custom.ini
to check how your plugin renders in the new navigation layout:
[feature_toggles]
enable = topnav
Migrate from onNavChanged
β
If your plugin uses the onNavChanged
callback to inform Grafana of its navigation model and child pages, you should see that this results in duplicated navigation elements. If you disable topnav
, then it should look just like before.
If topnav
is enabled, then we need to update the plugin to take advantage of the new PluginPage
component. In this case, we do not call onNavChanged
, which is now deprecated.
Switch to PluginPage
componentβ
Grafana now exposes a new PluginPage
component from @grafana/runtime
that hooks into the new navigation and page layouts. This new component also supports the old page layouts when the topnav
feature is disabled.
The new PluginPage
component will also handle rendering the section navigation. The section navigation can include other core sections and other plugins. To control what pages are displayed in the section navigation for a specific plugin, Grafana uses the pages that have been added in plugin.json
in which addToNav
was set to true
.
To use this component, simply wrap it around your page content:
import { PluginPage } from '@grafana/runtime';
...
return (
<PluginPage>
{your page content here}
</PluginPage>
);
Grafana looks at the URL to know what plugin and page should be active in the section nav. Accordingly, this component only works for pages that you have specified in plugin.json
. The PluginPage
will then render a page header based on the page name specified in plugin.json
.
Use PluginPage
for pages not defined in plugin.json
β
The PluginPage
component also exposes a pageNav
property that is a NavModelItem
. This pageNav
property is useful for pages that are not defined in plugin.json
(for example, individual item pages). The text
and description
that you specify in the pageNav
model are used to populate the breadcrumbs and the page header.
Example:
const pageNav = {
text: 'Write errors cortex-prod-04',
description: 'Incident timeline and details'
};
return (
<PluginPage pageNav={pageNav}>
{your page content here}
</PluginPage>
);
The way the active page is matched in the breadcrumbs and section nav relies on the page routes being hierarchical. If you have a list page and an item page, then you need to make the item page into a subroute of the list page. Furthermore, you also need to specify the list page URL in your plugin.json
.
For example, you might have a list of users at /users
. This means that the item page for a specific user needs to be at /users/:id
. This may require some refactoring of your routes.
Use PluginPage
with tabsβ
You can also create a further layer of hierarchy by specifying children
in the pageNav
model to create a page with tabbed navigation.
Example:
const pageNav = {
text: 'My page',
description: 'Incident timeline and details',
url: '/a/myorgid-pluginname-app',
children: [
{
url: '/a/myorgid-pluginname-app/tab1',
text: 'Tab1',
active: true,
},
{
url: '/a/myorgid-pluginname-app/tab2',
text: 'Tab1',
},
],
};
return (
<PluginPage pageNav={pageNav}>
{your page content here}
</PluginPage>
);
Use PluginPage
in a backwards-compatible wayβ
If you want to maintain backwards-compatibility with older versions of Grafana, one way is to implement a PluginPage
wrapper. If PluginPage
is available and the topnav
feature is enabled, then use the real PluginPage
. In other scenarios, fall back to whatever each plugin is doing today (such as making a call to onNavChanged
).
Example:
import { PluginPageProps, PluginPage as RealPluginPage, config } from '@grafana/runtime';
export const PluginPage = RealPluginPage && config.featureToggles.topnav ? RealPluginPage : PluginPageFallback;
function PluginPageFallback(props: PluginPageProps) {
return props.children;
}
Thereβs an additional step (and if
block) that is needed to hide or show tabs depending on whether config.features.topnav
is true
. Your plugin needs to include these changes in the useNavModel.ts
file:
// useNavModel.ts
import { config } from '@grafana/runtime';
...
export function useNavModel({ meta, rootPath, onNavChanged }: Args) {
const { pathname, search } = useLocation();
useEffect(() => {
if (config.featureToggles.topnav) {
return;
}
}, [config]);
...
Forwarded HTTP headers in the plugin SDK for Goβ
We recommended to use the <request>.GetHTTPHeader
or <request>.GetHTTPHeaders
methods when retrieving forwarded HTTP headers. See Forward OAuth identity for the logged-in user, Forward cookies for the logged-in user or Forward user header for the logged-in user for example usages.
Technical detailsβ
The Grafana SDK for Go v0.147.0 introduces a new interface ForwardHTTPHeaders that QueryDataRequest
, CheckHealthRequest
and CallResourceRequest
implements.
Newly introduced forwarded HTTP headers in Grafana v9.4.0 are X-Grafana-User
, X-Panel-Id
, X-Dashboard-Uid
, X-Datasource-Uid
and X-Grafana-Org-Id
. Internally, we prefix these with http_
and they are sent as http_<HTTP header name>
in CheckHealthRequest.Headers and QueryDataRequest.Headers.
We recommend using the ForwardHTTPHeaders methods so that you're guaranteed to be able to operate on HTTP headers without using the prefix. That is, you can operate on X-Grafana-User
, X-Panel-Id
, X-Dashboard-Uid
, X-Datasource-Uid
and X-Grafana-Org-Id
.