Files
grafana/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/QueryLinkExtension.tsx
Marcus Andersson 804c726413 PluginExtensions: Make the extensions registry reactive (#83085)
* feat: add a reactive extension registry

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>

* feat: add hooks to work with the reactive registry

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>

* feat: start using the reactive registry

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>

* feat: update the "command palette" extension point to use the hook

* feat: update the "alerting" extension point to use the hooks

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>

* feat: update the "explore" extension point to use the hooks

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>

* feat: update the "datasources config" extension point to use the hooks

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>

* feat: update the "panel menu" extension point to use the hooks

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>

* feat: update the "pyroscope datasource" extension point to use the hooks

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>

* feat: update the "user profile page" extension point to use the hooks

* chore: update betterer

* fix: update the hooks to not re-render unnecessarily

* chore: remove the old `createPluginExtensionRegistry` impementation

* chore: add "TODO" for `PanelMenuBehaviour` extension point

* feat: update the return value of the hooks to contain a `{ isLoading }` param

* tests: add more tests for the usePluginExtensions() hook

* fix: exclude the cloud-home-app from being non-awaited

* refactor: use uuidv4() for random ID generation (for the registry object)

* fix: linting issue

* feat: use the hooks for the new alerting extension point

* feat: use `useMemo()` for `AlertInstanceAction` extension point context

---------

Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
2024-04-24 09:33:16 +02:00

105 lines
3.0 KiB
TypeScript

import { css } from '@emotion/css';
import React from 'react';
import { useAsync } from 'react-use';
import { GrafanaTheme2, QueryEditorProps, TimeRange } from '@grafana/data';
import { getBackendSrv, usePluginLinkExtensions } from '@grafana/runtime';
import { LinkButton, useStyles2 } from '@grafana/ui';
import { PyroscopeDataSource } from '../datasource';
import { PyroscopeDataSourceOptions, Query } from '../types';
const EXTENSION_POINT_ID = 'plugins/grafana-pyroscope-datasource/query-links';
/** A subset of the datasource settings that are relevant for this integration */
type PyroscopeDatasourceSettings = {
uid: string;
url: string;
type: string;
basicAuthUser: string;
};
/** The context object that will be shared with the link extension's configure function */
type ExtensionQueryLinksContext = {
datasourceUid: string;
query: Query;
range?: TimeRange | undefined;
datasourceSettings?: PyroscopeDatasourceSettings;
};
/* Global promises to fetch pyroscope datasource settings by uid as encountered */
const pyroscopeDatasourceSettingsByUid: Record<string, PyroscopeDatasourceSettings> = {};
/* Reset promises for testing purposes */
export function resetPyroscopeQueryLinkExtensionsFetches() {
Object.keys(pyroscopeDatasourceSettingsByUid).forEach((key) => delete pyroscopeDatasourceSettingsByUid[key]);
}
/** A subset of the `PyroscopeDataSource` `QueryEditorProps` */
export type Props = Pick<
QueryEditorProps<PyroscopeDataSource, Query, PyroscopeDataSourceOptions>,
'datasource' | 'query' | 'range'
>;
export function PyroscopeQueryLinkExtensions(props: Props) {
const {
datasource: { uid: datasourceUid },
query,
range,
} = props;
const { value: datasourceSettings } = useAsync(async () => {
if (pyroscopeDatasourceSettingsByUid[datasourceUid]) {
return pyroscopeDatasourceSettingsByUid[datasourceUid];
}
const settings = await getBackendSrv().get<PyroscopeDatasourceSettings>(`/api/datasources/uid/${datasourceUid}`);
pyroscopeDatasourceSettingsByUid[datasourceUid] = settings;
return settings;
}, [datasourceUid]);
const context: ExtensionQueryLinksContext = {
datasourceUid,
query,
range,
datasourceSettings,
};
const { extensions } = usePluginLinkExtensions({
extensionPointId: EXTENSION_POINT_ID,
context,
});
const styles = useStyles2(getStyles);
if (extensions.length === 0) {
return null;
}
return (
<>
{extensions.map((extension) => (
<LinkButton
className={styles.linkButton}
key={`${extension.id}`}
variant="secondary"
icon={extension.icon || 'external-link-alt'}
tooltip={extension.description}
target="_blank"
href={extension.path}
onClick={extension.onClick}
>
{extension.title}
</LinkButton>
))}
</>
);
}
function getStyles(theme: GrafanaTheme2) {
return {
linkButton: css({
marginLeft: theme.spacing(1),
}),
};
}