
* initial restructuring * add weights for ordering * fix links * remove aliases, add script Co-authored-by: Jack Baldry <jack.baldry@grafana.com> Co-authored-by: David Harris <david.harris@grafana.com> * make pretty * use correct link for angular to react guide * refactor: move the migration guide to /Developers * Revert "refactor: move the migration guide to /Developers" This reverts commit 8f31af8dfde3399060e8023c635174eb1243ae72. * fix: only redirect if the URL-hash has a valid redirect * fix: use a rel-ref for the Angular-React migration guide link * fix: update the hash redirects --------- Co-authored-by: David Harris <david.harris@grafana.com> Co-authored-by: Jack Baldry <jack.baldry@grafana.com>
6.5 KiB
description | keywords | title | menutitle | weight | |||||
---|---|---|---|---|---|---|---|---|---|
Guide for migrating plugins from Grafana v9.3.x to v9.4.x |
|
Migrating plugins from Grafana 9.3.x to 9.4.x | v9.3.x to v9.4.x | 2000 |
Migrating plugins from Grafana 9.3.x to 9.4.x
Supporting new navigation layout
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 nav model & sub pages, you should see that this results in duplicated navigation elements. If you disable topnav
it should look just as before.
When topnav
is enabled we need to update the plugin to take advantage of the new PluginPage
component and not call onNavChanged
. onNavChanged
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 and supports the old page layouts when the topnav
feature is disabled. This new 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 will use the pages added in plugin.json
that have addToNav
set to true
.
This component is very easy to use. Simply wrap it around your page content:
import { PluginPage } from '@grafana/runtime';
...
return (
<PluginPage>
{your page content here}
</PluginPage>
);
Grafana will look at the URL to know what plugin and page should be active in the section nav, so this only works for pages you have specified in plugin.json
. PluginPage
will then render a page header based on the page name specified in plugin.json
.
Using 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
(e.g. individual item pages). The text
and description
you specify in the pageNav
model will be used to populate the breadcrumbs and 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, the item page needs to be a subroute of the list page and the list page url needs to be specified 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.
Using PluginPage
with tabs
You can also create a further layer of hierarchy by specifying children
in the pageNav
model to created 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>
);
Using 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
, otherwise fallback to whatever each plugin is doing today (including calling 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) needed to hide/show tabs depending on if config.features.topnav
is true
. These changes will need to be made in the useNavModel.ts
file in your plugin:
// 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 grafana-plugin-sdk-go
It's recommended to use the <request>.GetHTTPHeader
or <request>.GetHTTPHeaders
methods when retrieving forwarded HTTP headers. See [Forward OAuth identity for the logged-in user]({{< relref "add-authentication-for-data-source-plugins.md#forward-oauth-identity-for-the-logged-in-user" >}}), [Forward cookies for the logged-in user
]({{< relref "add-authentication-for-data-source-plugins.md#forward-cookies-for-the-logged-in-user" >}}) or [Forward user header for the logged-in user]({{< relref "add-authentication-for-data-source-plugins.md#forward-user-header-for-the-logged-in-user" >}}) for example usages.
Technical details
The grafana-plugin-sdk-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 these are prefixed with http_
and sent as http_<HTTP header name>
in CheckHealthRequest.Headers and QueryDataRequest.Headers. By using the ForwardHTTPHeaders methods you're guaranteed to be able to operate on HTTP headers without using the prefix, i.e. X-Grafana-User
, X-Panel-Id
, X-Dashboard-Uid
, X-Datasource-Uid
and X-Grafana-Org-Id
.