mirror of https://github.com/grafana/grafana
fix: pyroscope datasource remove link extension (#89512)
It has been decided that we will not be using this moving forward.pull/89633/head
parent
7d8afd9578
commit
f5d9c247d9
@ -1,131 +0,0 @@ |
|||||||
import { render, screen } from '@testing-library/react'; |
|
||||||
import React from 'react'; |
|
||||||
import { act } from 'react-dom/test-utils'; |
|
||||||
|
|
||||||
import { PluginType, rangeUtil, PluginExtensionLink, PluginExtensionTypes } from '@grafana/data'; |
|
||||||
import { usePluginLinkExtensions } from '@grafana/runtime'; |
|
||||||
|
|
||||||
import { PyroscopeDataSource } from '../datasource'; |
|
||||||
import { mockFetchPyroscopeDatasourceSettings } from '../datasource.test'; |
|
||||||
|
|
||||||
import { Props, PyroscopeQueryLinkExtensions, resetPyroscopeQueryLinkExtensionsFetches } from './QueryLinkExtension'; |
|
||||||
|
|
||||||
// Constants copied from `QueryLinkExtension.tsx`
|
|
||||||
const EXTENSION_POINT_ID = 'plugins/grafana-pyroscope-datasource/query-links'; |
|
||||||
|
|
||||||
jest.mock('@grafana/runtime', () => ({ |
|
||||||
...jest.requireActual('@grafana/runtime'), |
|
||||||
usePluginLinkExtensions: jest.fn(), |
|
||||||
getTemplateSrv: () => { |
|
||||||
return { |
|
||||||
replace: (query: string): string => { |
|
||||||
return query.replace(/\$var/g, 'interpolated'); |
|
||||||
}, |
|
||||||
}; |
|
||||||
}, |
|
||||||
})); |
|
||||||
|
|
||||||
const usePluginLinkExtensionsMock = jest.mocked(usePluginLinkExtensions); |
|
||||||
|
|
||||||
const defaultPyroscopeDataSourceSettings = { |
|
||||||
uid: 'default-pyroscope', |
|
||||||
url: 'http://pyroscope', |
|
||||||
basicAuthUser: 'pyroscope_user', |
|
||||||
}; |
|
||||||
|
|
||||||
describe('PyroscopeQueryLinkExtensions', () => { |
|
||||||
const EXPECTED_BUTTON_LABEL = 'Profiles App'; |
|
||||||
const DEFAULT_EXTENSION_PATH = 'a/mock-path-app/fake-path'; |
|
||||||
|
|
||||||
function createExtension(overrides?: Partial<PluginExtensionLink>) { |
|
||||||
return { |
|
||||||
...{ |
|
||||||
description: 'unremarkable-description', |
|
||||||
extensionPointId: EXTENSION_POINT_ID, |
|
||||||
title: EXPECTED_BUTTON_LABEL, |
|
||||||
path: DEFAULT_EXTENSION_PATH, |
|
||||||
type: PluginExtensionTypes.link, |
|
||||||
category: 'unremarkable-category', |
|
||||||
icon: 'heart', |
|
||||||
onClick() {}, |
|
||||||
pluginId: 'mock-path-app', |
|
||||||
id: `${Date.now()}}`, |
|
||||||
}, |
|
||||||
...overrides, |
|
||||||
} as PluginExtensionLink; |
|
||||||
} |
|
||||||
|
|
||||||
beforeEach(() => { |
|
||||||
resetPyroscopeQueryLinkExtensionsFetches(); |
|
||||||
mockFetchPyroscopeDatasourceSettings(defaultPyroscopeDataSourceSettings); |
|
||||||
|
|
||||||
usePluginLinkExtensionsMock.mockRestore(); |
|
||||||
usePluginLinkExtensionsMock.mockReturnValue({ extensions: [], isLoading: false }); // Unless stated otherwise, no extensions
|
|
||||||
}); |
|
||||||
|
|
||||||
it('should render if extension present', async () => { |
|
||||||
usePluginLinkExtensionsMock.mockReturnValue({ extensions: [createExtension()], isLoading: false }); // Default extension
|
|
||||||
|
|
||||||
await act(setup); |
|
||||||
expect(await screen.findAllByText(EXPECTED_BUTTON_LABEL)).toBeDefined(); |
|
||||||
}); |
|
||||||
|
|
||||||
it('Should not render if no extension present', async () => { |
|
||||||
await act(setup); |
|
||||||
expect(screen.queryByText(EXPECTED_BUTTON_LABEL)).toBeNull(); |
|
||||||
}); |
|
||||||
}); |
|
||||||
|
|
||||||
function setupDs() { |
|
||||||
const ds = new PyroscopeDataSource({ |
|
||||||
...defaultPyroscopeDataSourceSettings, |
|
||||||
name: 'test', |
|
||||||
type: PluginType.datasource, |
|
||||||
access: 'proxy', |
|
||||||
id: 1, |
|
||||||
jsonData: {}, |
|
||||||
meta: { |
|
||||||
name: '', |
|
||||||
id: '', |
|
||||||
type: PluginType.datasource, |
|
||||||
baseUrl: '', |
|
||||||
info: { |
|
||||||
author: { |
|
||||||
name: '', |
|
||||||
}, |
|
||||||
description: '', |
|
||||||
links: [], |
|
||||||
logos: { |
|
||||||
large: '', |
|
||||||
small: '', |
|
||||||
}, |
|
||||||
screenshots: [], |
|
||||||
updated: '', |
|
||||||
version: '', |
|
||||||
}, |
|
||||||
module: '', |
|
||||||
}, |
|
||||||
readOnly: false, |
|
||||||
}); |
|
||||||
|
|
||||||
return ds; |
|
||||||
} |
|
||||||
|
|
||||||
async function setup(options: { props: Partial<Props> } = { props: {} }) { |
|
||||||
const utils = render( |
|
||||||
<PyroscopeQueryLinkExtensions |
|
||||||
query={{ |
|
||||||
queryType: 'both', |
|
||||||
labelSelector: '', |
|
||||||
profileTypeId: 'process_cpu:cpu', |
|
||||||
refId: 'A', |
|
||||||
maxNodes: 1000, |
|
||||||
groupBy: [], |
|
||||||
}} |
|
||||||
datasource={setupDs()} |
|
||||||
range={rangeUtil.convertRawToRange({ from: 'now-1h', to: 'now' })} |
|
||||||
{...options.props} |
|
||||||
/> |
|
||||||
); |
|
||||||
return { ...utils }; |
|
||||||
} |
|
||||||
@ -1,104 +0,0 @@ |
|||||||
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), |
|
||||||
}), |
|
||||||
}; |
|
||||||
} |
|
||||||
Loading…
Reference in new issue