mirror of
https://github.com/grafana/grafana.git
synced 2024-12-22 15:13:38 -06:00
Adapt plugin page to preinstalled plugins (#91874)
This commit is contained in:
parent
340af8cf6b
commit
44290ddf32
@ -42,6 +42,11 @@ export type AppPluginConfig = {
|
||||
angular: AngularMeta;
|
||||
};
|
||||
|
||||
export type PreinstalledPlugin = {
|
||||
id: string;
|
||||
version: string;
|
||||
};
|
||||
|
||||
export class GrafanaBootConfig implements GrafanaConfig {
|
||||
publicDashboardAccessToken?: string;
|
||||
publicDashboardsEnabled = true;
|
||||
@ -124,6 +129,7 @@ export class GrafanaBootConfig implements GrafanaConfig {
|
||||
pluginAdminExternalManageEnabled = false;
|
||||
pluginCatalogHiddenPlugins: string[] = [];
|
||||
pluginCatalogManagedPlugins: string[] = [];
|
||||
pluginCatalogPreinstalledPlugins: PreinstalledPlugin[] = [];
|
||||
pluginsCDNBaseURL = '';
|
||||
expressionsEnabled = false;
|
||||
customTheme?: undefined;
|
||||
|
@ -228,6 +228,7 @@ type FrontendSettingsDTO struct {
|
||||
PluginAdminExternalManageEnabled bool `json:"pluginAdminExternalManageEnabled"`
|
||||
PluginCatalogHiddenPlugins []string `json:"pluginCatalogHiddenPlugins"`
|
||||
PluginCatalogManagedPlugins []string `json:"pluginCatalogManagedPlugins"`
|
||||
PluginCatalogPreinstalledPlugins []setting.InstallPlugin `json:"pluginCatalogPreinstalledPlugins"`
|
||||
ExpressionsEnabled bool `json:"expressionsEnabled"`
|
||||
AwsAllowedAuthProviders []string `json:"awsAllowedAuthProviders"`
|
||||
AwsAssumeRoleEnabled bool `json:"awsAssumeRoleEnabled"`
|
||||
|
@ -274,6 +274,7 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro
|
||||
PluginAdminExternalManageEnabled: hs.Cfg.PluginAdminEnabled && hs.Cfg.PluginAdminExternalManageEnabled,
|
||||
PluginCatalogHiddenPlugins: hs.Cfg.PluginCatalogHiddenPlugins,
|
||||
PluginCatalogManagedPlugins: hs.managedPluginsService.ManagedPlugins(c.Req.Context()),
|
||||
PluginCatalogPreinstalledPlugins: hs.Cfg.InstallPlugins,
|
||||
ExpressionsEnabled: hs.Cfg.ExpressionsEnabled,
|
||||
AwsAllowedAuthProviders: hs.Cfg.AWSAllowedAuthProviders,
|
||||
AwsAssumeRoleEnabled: hs.Cfg.AWSAssumeRoleEnabled,
|
||||
|
@ -524,8 +524,8 @@ type Cfg struct {
|
||||
}
|
||||
|
||||
type InstallPlugin struct {
|
||||
ID string
|
||||
Version string
|
||||
ID string `json:"id"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// AddChangePasswordLink returns if login form is disabled or not since
|
||||
|
@ -21,6 +21,7 @@ export default {
|
||||
isDeprecated: false,
|
||||
isPublished: true,
|
||||
isManaged: false,
|
||||
isPreinstalled: { found: false, withVersion: false },
|
||||
name: 'Zabbix',
|
||||
orgName: 'Alexander Zobnin',
|
||||
popularity: 0.2093,
|
||||
|
@ -33,6 +33,7 @@ const plugin: CatalogPlugin = {
|
||||
isDeprecated: false,
|
||||
isPublished: true,
|
||||
isManaged: false,
|
||||
isPreinstalled: { found: false, withVersion: false },
|
||||
};
|
||||
|
||||
describe('GetStartedWithDataSource', () => {
|
||||
|
@ -33,6 +33,7 @@ const plugin: CatalogPlugin = {
|
||||
isDeprecated: false,
|
||||
isPublished: true,
|
||||
isManaged: false,
|
||||
isPreinstalled: { found: false, withVersion: false },
|
||||
};
|
||||
|
||||
function setup(opts: { angularSupportEnabled: boolean; angularDetected: boolean }) {
|
||||
@ -253,5 +254,17 @@ describe('InstallControlsButton', () => {
|
||||
);
|
||||
expect(screen.queryByText('Update')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should be hidden when plugin is preinstalled with a specific version', () => {
|
||||
render(
|
||||
<TestProvider>
|
||||
<InstallControlsButton
|
||||
plugin={{ ...plugin, isPreinstalled: { found: true, withVersion: true } }}
|
||||
pluginStatus={PluginStatus.UPDATE}
|
||||
/>
|
||||
</TestProvider>
|
||||
);
|
||||
expect(screen.queryByText('Update')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -113,12 +113,17 @@ export function InstallControlsButton({
|
||||
}
|
||||
};
|
||||
|
||||
if (pluginStatus === PluginStatus.UNINSTALL) {
|
||||
const disableUninstall =
|
||||
config.pluginAdminExternalManageEnabled && configCore.featureToggles.managedPluginsInstall
|
||||
? plugin.isUninstallingFromInstance
|
||||
: isUninstalling;
|
||||
let disableUninstall =
|
||||
config.pluginAdminExternalManageEnabled && configCore.featureToggles.managedPluginsInstall
|
||||
? plugin.isUninstallingFromInstance
|
||||
: isUninstalling;
|
||||
let uninstallTitle = '';
|
||||
if (plugin.isPreinstalled.found) {
|
||||
disableUninstall = true;
|
||||
uninstallTitle = 'Preinstalled plugin. Remove from Grafana config before uninstalling.';
|
||||
}
|
||||
|
||||
if (pluginStatus === PluginStatus.UNINSTALL) {
|
||||
return (
|
||||
<>
|
||||
<ConfirmModal
|
||||
@ -131,7 +136,7 @@ export function InstallControlsButton({
|
||||
onDismiss={hideConfirmModal}
|
||||
/>
|
||||
<Stack alignItems="flex-start" width="auto" height="auto">
|
||||
<Button variant="destructive" disabled={disableUninstall} onClick={showConfirmModal}>
|
||||
<Button variant="destructive" disabled={disableUninstall} onClick={showConfirmModal} title={uninstallTitle}>
|
||||
{uninstallBtnText}
|
||||
</Button>
|
||||
</Stack>
|
||||
@ -152,12 +157,12 @@ export function InstallControlsButton({
|
||||
|
||||
return (
|
||||
<Stack alignItems="flex-start" width="auto" height="auto">
|
||||
{!plugin.isManaged && (
|
||||
{!plugin.isManaged && !plugin.isPreinstalled.withVersion && (
|
||||
<Button disabled={disableUpdate} onClick={onUpdate}>
|
||||
{isInstalling ? 'Updating' : 'Update'}
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="destructive" disabled={isUninstalling} onClick={onUninstall}>
|
||||
<Button variant="destructive" disabled={disableUninstall} onClick={onUninstall} title={uninstallTitle}>
|
||||
{uninstallBtnText}
|
||||
</Button>
|
||||
</Stack>
|
||||
|
@ -58,6 +58,7 @@ describe('PluginListItem', () => {
|
||||
isDeprecated: false,
|
||||
isPublished: true,
|
||||
isManaged: false,
|
||||
isPreinstalled: { found: false, withVersion: false },
|
||||
};
|
||||
|
||||
/** As Grid */
|
||||
|
@ -34,6 +34,7 @@ describe('PluginListItemBadges', () => {
|
||||
isDeprecated: false,
|
||||
isPublished: true,
|
||||
isManaged: false,
|
||||
isPreinstalled: { found: false, withVersion: false },
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
@ -84,6 +85,20 @@ describe('PluginListItemBadges', () => {
|
||||
expect(screen.queryByText(/update available/i)).toBeNull();
|
||||
});
|
||||
|
||||
it('does not render an upgrade badge (when plugin is preinstalled with a version)', () => {
|
||||
render(
|
||||
<PluginListItemBadges
|
||||
plugin={{
|
||||
...plugin,
|
||||
hasUpdate: true,
|
||||
installedVersion: '0.0.9',
|
||||
isPreinstalled: { found: true, withVersion: true },
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expect(screen.queryByText(/update available/i)).toBeNull();
|
||||
});
|
||||
|
||||
it('renders an angular badge (when plugin is angular)', () => {
|
||||
render(<PluginListItemBadges plugin={{ ...plugin, angularDetected: true }} />);
|
||||
expect(screen.getByText(/angular/i)).toBeVisible();
|
||||
|
@ -24,7 +24,9 @@ export function PluginListItemBadges({ plugin }: PluginBadgeType) {
|
||||
<Stack height="auto" wrap="wrap">
|
||||
<PluginEnterpriseBadge plugin={plugin} />
|
||||
{plugin.isDisabled && <PluginDisabledBadge error={plugin.error} />}
|
||||
{hasUpdate && !plugin.isManaged && <PluginUpdateAvailableBadge plugin={plugin} />}
|
||||
{hasUpdate && !plugin.isManaged && !plugin.isPreinstalled.withVersion && (
|
||||
<PluginUpdateAvailableBadge plugin={plugin} />
|
||||
)}
|
||||
{plugin.angularDetected && <PluginAngularBadge />}
|
||||
</Stack>
|
||||
);
|
||||
@ -36,7 +38,9 @@ export function PluginListItemBadges({ plugin }: PluginBadgeType) {
|
||||
{plugin.isDisabled && <PluginDisabledBadge error={plugin.error} />}
|
||||
{plugin.isDeprecated && <PluginDeprecatedBadge />}
|
||||
{plugin.isInstalled && <PluginInstalledBadge />}
|
||||
{hasUpdate && !plugin.isManaged && <PluginUpdateAvailableBadge plugin={plugin} />}
|
||||
{hasUpdate && !plugin.isManaged && !plugin.isPreinstalled.withVersion && (
|
||||
<PluginUpdateAvailableBadge plugin={plugin} />
|
||||
)}
|
||||
{plugin.angularDetected && <PluginAngularBadge />}
|
||||
</Stack>
|
||||
);
|
||||
|
@ -204,6 +204,7 @@ describe('Plugins/Helpers', () => {
|
||||
isDeprecated: false,
|
||||
isPublished: true,
|
||||
isManaged: false,
|
||||
isPreinstalled: { found: false, withVersion: false },
|
||||
name: 'Zabbix',
|
||||
orgName: 'Alexander Zobnin',
|
||||
popularity: 0.2111,
|
||||
@ -282,6 +283,7 @@ describe('Plugins/Helpers', () => {
|
||||
isPublished: false,
|
||||
isDeprecated: false,
|
||||
isManaged: false,
|
||||
isPreinstalled: { found: false, withVersion: false },
|
||||
name: 'Zabbix',
|
||||
orgName: 'Alexander Zobnin',
|
||||
popularity: 0,
|
||||
@ -335,6 +337,7 @@ describe('Plugins/Helpers', () => {
|
||||
isPublished: true,
|
||||
isDeprecated: false,
|
||||
isManaged: false,
|
||||
isPreinstalled: { found: false, withVersion: false },
|
||||
name: 'Zabbix',
|
||||
orgName: 'Alexander Zobnin',
|
||||
popularity: 0.2111,
|
||||
|
@ -143,6 +143,7 @@ export function mapRemoteToCatalog(plugin: RemotePlugin, error?: PluginError): C
|
||||
isInstalled: isDisabled,
|
||||
isDisabled: isDisabled,
|
||||
isManaged: isManagedPlugin(id),
|
||||
isPreinstalled: isPreinstalledPlugin(id),
|
||||
isDeprecated: status === RemotePluginStatus.Deprecated,
|
||||
isCore: plugin.internal,
|
||||
isDev: false,
|
||||
@ -193,6 +194,7 @@ export function mapLocalToCatalog(plugin: LocalPlugin, error?: PluginError): Cat
|
||||
isDev: Boolean(dev),
|
||||
isEnterprise: false,
|
||||
isManaged: isManagedPlugin(id),
|
||||
isPreinstalled: isPreinstalledPlugin(id),
|
||||
type,
|
||||
error: error?.errorCode,
|
||||
accessControl: accessControl,
|
||||
@ -241,6 +243,7 @@ export function mapToCatalogPlugin(local?: LocalPlugin, remote?: RemotePlugin, e
|
||||
isDeprecated: remote?.status === RemotePluginStatus.Deprecated,
|
||||
isPublished: true,
|
||||
isManaged: isManagedPlugin(id),
|
||||
isPreinstalled: isPreinstalledPlugin(id),
|
||||
// TODO<check if we would like to keep preferring the remote version>
|
||||
name: remote?.name || local?.name || '',
|
||||
// TODO<check if we would like to keep preferring the remote version>
|
||||
@ -382,6 +385,13 @@ export function isManagedPlugin(id: string) {
|
||||
return pluginCatalogManagedPlugins?.includes(id);
|
||||
}
|
||||
|
||||
export function isPreinstalledPlugin(id: string): { found: boolean; withVersion: boolean } {
|
||||
const { pluginCatalogPreinstalledPlugins } = config;
|
||||
|
||||
const plugin = pluginCatalogPreinstalledPlugins?.find((p) => p.id === id);
|
||||
return { found: !!plugin?.id, withVersion: !!plugin?.version };
|
||||
}
|
||||
|
||||
function isDisabledSecretsPlugin(type?: PluginType): boolean {
|
||||
return type === PluginType.secretsmanager && !config.secretsManagerPluginEnabled;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ export const usePluginInfo = (plugin?: CatalogPlugin): PageInfoItem[] => {
|
||||
version = latestCompatibleVersion?.version;
|
||||
}
|
||||
|
||||
if (Boolean(version)) {
|
||||
if (version) {
|
||||
if (plugin.isManaged) {
|
||||
info.push({
|
||||
label: t('plugins.details.labels.version', 'Version'),
|
||||
@ -34,7 +34,7 @@ export const usePluginInfo = (plugin?: CatalogPlugin): PageInfoItem[] => {
|
||||
} else {
|
||||
info.push({
|
||||
label: t('plugins.details.labels.version', 'Version'),
|
||||
value: version,
|
||||
value: `${version}${plugin.isPreinstalled.withVersion ? ' (preinstalled)' : ''}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -326,6 +326,24 @@ describe('Plugin details page', () => {
|
||||
expect(queryByRole('button', { name: /^install/i })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not display an update button for a plugin that is pre installed', async () => {
|
||||
const { queryByRole, getByText } = renderPluginDetails({
|
||||
id,
|
||||
isInstalled: true,
|
||||
hasUpdate: true,
|
||||
isPreinstalled: { found: true, withVersion: true },
|
||||
});
|
||||
|
||||
// Does not display an "update" button
|
||||
expect(await queryByRole('button', { name: /update/i })).not.toBeInTheDocument();
|
||||
|
||||
// Does not display "install" button
|
||||
expect(queryByRole('button', { name: /^install/i })).not.toBeInTheDocument();
|
||||
|
||||
// Display an uninstall button but disabled
|
||||
expect(getByText(/Uninstall/i).closest('button')).toBeDisabled();
|
||||
});
|
||||
|
||||
it('should display an install button for enterprise plugins if license is valid', async () => {
|
||||
config.licenseInfo.enabledFeatures = { 'enterprise.plugins': true };
|
||||
|
||||
|
@ -40,6 +40,7 @@ export interface CatalogPlugin extends WithAccessControlMetadata {
|
||||
isDisabled: boolean;
|
||||
isDeprecated: boolean;
|
||||
isManaged: boolean; // Indicates that the plugin version is managed by Grafana
|
||||
isPreinstalled: { found: boolean; withVersion: boolean }; // Indicates that the plugin is pre-installed
|
||||
// `isPublished` is TRUE if the plugin is published to grafana.com
|
||||
isPublished: boolean;
|
||||
name: string;
|
||||
|
Loading…
Reference in New Issue
Block a user