The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/public/app/features/plugins/admin/helpers.test.ts

692 lines
25 KiB

import { PluginSignatureStatus, PluginSignatureType, PluginType } from '@grafana/data';
import { config } from '@grafana/runtime';
import { getLocalPluginMock, getRemotePluginMock, getCatalogPluginMock } from './__mocks__';
import {
mapToCatalogPlugin,
mapRemoteToCatalog,
mapLocalToCatalog,
mergeLocalAndRemote,
mergeLocalsAndRemotes,
sortPlugins,
Sorters,
isLocalPluginVisible,
isRemotePluginVisible,
} from './helpers';
import { RemotePlugin, LocalPlugin } from './types';
describe('Plugins/Helpers', () => {
let remotePlugin: RemotePlugin;
let localPlugin: LocalPlugin;
beforeEach(() => {
remotePlugin = getRemotePluginMock();
localPlugin = getLocalPluginMock();
});
describe('mergeLocalsAndRemotes()', () => {
const localPlugins = [
getLocalPluginMock({ id: 'plugin-1' }),
getLocalPluginMock({ id: 'plugin-2' }),
getLocalPluginMock({ id: 'plugin-3' }), // only on local
];
const remotePlugins = [
getRemotePluginMock({ slug: 'plugin-1' }),
getRemotePluginMock({ slug: 'plugin-2' }),
getRemotePluginMock({ slug: 'plugin-4' }), // only on remote
];
test('adds all available plugins only once', () => {
const merged = mergeLocalsAndRemotes(localPlugins, remotePlugins);
const mergedIds = merged.map(({ id }) => id);
expect(merged.length).toBe(4);
expect(mergedIds).toContain('plugin-1');
expect(mergedIds).toContain('plugin-2');
expect(mergedIds).toContain('plugin-3');
expect(mergedIds).toContain('plugin-4');
});
test('merges all plugins with their counterpart (if available)', () => {
const merged = mergeLocalsAndRemotes(localPlugins, remotePlugins);
const findMerged = (mergedId: string) => merged.find(({ id }) => id === mergedId);
// Both local & remote counterparts
expect(findMerged('plugin-1')).toEqual(
mergeLocalAndRemote(getLocalPluginMock({ id: 'plugin-1' }), getRemotePluginMock({ slug: 'plugin-1' }))
);
expect(findMerged('plugin-2')).toEqual(
mergeLocalAndRemote(getLocalPluginMock({ id: 'plugin-2' }), getRemotePluginMock({ slug: 'plugin-2' }))
);
// Only local
expect(findMerged('plugin-3')).toEqual(mergeLocalAndRemote(getLocalPluginMock({ id: 'plugin-3' })));
// Only remote
expect(findMerged('plugin-4')).toEqual(mergeLocalAndRemote(undefined, getRemotePluginMock({ slug: 'plugin-4' })));
});
});
describe('mergeLocalAndRemote()', () => {
test('merges using mapRemoteToCatalog() if there is only a remote version', () => {
expect(mergeLocalAndRemote(undefined, remotePlugin)).toEqual(mapRemoteToCatalog(remotePlugin));
});
test('merges using mapLocalToCatalog() if there is only a local version', () => {
expect(mergeLocalAndRemote(localPlugin)).toEqual(mapLocalToCatalog(localPlugin));
});
test('merges using mapToCatalogPlugin() if there is both a remote and a local version', () => {
expect(mergeLocalAndRemote(localPlugin, remotePlugin)).toEqual(mapToCatalogPlugin(localPlugin, remotePlugin));
});
});
describe('mapRemoteToCatalog()', () => {
test('maps the remote response (GCOM /api/plugins/<id>) to PluginCatalog', () => {
expect(mapRemoteToCatalog(remotePlugin)).toEqual({
description: 'Zabbix plugin for Grafana',
downloads: 33645089,
hasUpdate: false,
id: 'alexanderzobnin-zabbix-app',
info: {
logos: {
large: 'https://grafana.com/api/plugins/alexanderzobnin-zabbix-app/versions/4.1.5/logos/large',
small: 'https://grafana.com/api/plugins/alexanderzobnin-zabbix-app/versions/4.1.5/logos/small',
},
},
error: undefined,
isCore: false,
isDev: false,
isDisabled: false,
isEnterprise: false,
isInstalled: false,
isPublished: true,
name: 'Zabbix',
orgName: 'Alexander Zobnin',
popularity: 0.2111,
publishedAt: '2016-04-06T20:23:41.000Z',
signature: 'valid',
type: 'app',
updatedAt: '2021-05-18T14:53:01.000Z',
});
});
test('adds the correct signature enum', () => {
const pluginWithoutSignature = { ...remotePlugin, signatureType: '', versionSignatureType: '' } as RemotePlugin;
// With only "signatureType" -> valid
const pluginWithSignature1 = { ...remotePlugin, signatureType: PluginSignatureType.commercial } as RemotePlugin;
// With only "versionSignatureType" -> valid
const pluginWithSignature2 = { ...remotePlugin, versionSignatureType: PluginSignatureType.core } as RemotePlugin;
expect(mapRemoteToCatalog(pluginWithoutSignature).signature).toBe(PluginSignatureStatus.missing);
expect(mapRemoteToCatalog(pluginWithSignature1).signature).toBe(PluginSignatureStatus.valid);
expect(mapRemoteToCatalog(pluginWithSignature2).signature).toBe(PluginSignatureStatus.valid);
});
test('adds an "isEnterprise" field', () => {
const enterprisePlugin = { ...remotePlugin, status: 'enterprise' } as RemotePlugin;
const notEnterprisePlugin = { ...remotePlugin, status: 'unknown' } as RemotePlugin;
expect(mapRemoteToCatalog(enterprisePlugin).isEnterprise).toBe(true);
expect(mapRemoteToCatalog(notEnterprisePlugin).isEnterprise).toBe(false);
});
test('adds an "isCore" field', () => {
const corePlugin = { ...remotePlugin, internal: true } as RemotePlugin;
const notCorePlugin = { ...remotePlugin, internal: false } as RemotePlugin;
expect(mapRemoteToCatalog(corePlugin).isCore).toBe(true);
expect(mapRemoteToCatalog(notCorePlugin).isCore).toBe(false);
});
});
describe('mapLocalToCatalog()', () => {
test('maps local response to PluginCatalog', () => {
expect(mapLocalToCatalog(localPlugin)).toEqual({
description: 'Zabbix plugin for Grafana',
downloads: 0,
id: 'alexanderzobnin-zabbix-app',
info: {
logos: {
large: 'public/plugins/alexanderzobnin-zabbix-app/img/icn-zabbix-app.svg',
small: 'public/plugins/alexanderzobnin-zabbix-app/img/icn-zabbix-app.svg',
},
},
error: undefined,
hasUpdate: false,
isCore: false,
isDev: false,
isDisabled: false,
isEnterprise: false,
isInstalled: true,
isPublished: false,
name: 'Zabbix',
orgName: 'Alexander Zobnin',
popularity: 0,
publishedAt: '',
signature: 'valid',
signatureOrg: 'Alexander Zobnin',
signatureType: 'community',
type: 'app',
updatedAt: '2021-08-25',
Plugins Catalog: Install and show the latest compatible version of a plugin (#41003) * fix(catalog): prefer rendering installed version over latest version * feat(catalog): signify installed version in version history * feat(catalog): introduce installedVersion and latestVersion * refactor(catalog): use latestVersion for installation, simplify plugindetails header logic * refactor(catalog): clean up installedVersion and latestVersion * feat(catalog): use table-layout so versions list table has consistent column widths * test(catalog): update failing tests * removed the need of having a latest version in the plugin catalog type root level. * fixed flaky test depending on what locale it was being running with. * added missing test to verify version for a remote plugin. * fixed version in header. * preventing the UI from break if no versions are available. * fixed failing test due to missing mock data. * added todo as a reminder. * refactor(catalog): prefer grafana plugin icons over gcom notfound images * refactor(Plugins/Admin): change constant name * refactor(Plugins/Admin): add comment to make condition easier to understand * chore: update go modules * feat(Backend/Plugins): add "dependencies" field to `PluginListItem` * feat(Plugins/Admin): show the grafana dependency for the installed version * refactor(Plugins/Admin): use the local version of links * refactor(Plugins/Admin): prefer the local version for `.type` * refactor(Plugins/ADmin): prefer the local `.description` field * fix(Plugins/Admin): fix tests * test(plugins/api): update the expected response for the `api/plugins` tests * chore(Plugins/Admin): add todo comments to check preferation of remote/local values * feat(backend/api): always send the grafana version as a header when proxying to GCOM * feat(plugins/admin): use the `isCompatible` flag to get the latest compatible version * feat(plugins/admin): show the latest compatible version in the versions list * fix(plugins/admin): show the grafana dependency for the latest compatible version * fix(plugins/admin): update the version list when installing/uninstalling a plugin * test(plugins/admin): add some test-cases for the latest-compatible-version * fix(plugins/admin): show the grafana dependency for the installed version (if installed) * feat(plugins/backend): add the `dependencies.grafanaDependency` property to the plugin object * test(plugins/backend): fix tests by adjusting expected response json Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com> Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
4 years ago
installedVersion: '4.2.2',
});
});
test('isCore if signature is internal', () => {
const pluginWithoutInternalSignature = { ...localPlugin };
const pluginWithInternalSignature = { ...localPlugin, signature: 'internal' } as LocalPlugin;
expect(mapLocalToCatalog(pluginWithoutInternalSignature).isCore).toBe(false);
expect(mapLocalToCatalog(pluginWithInternalSignature).isCore).toBe(true);
});
test('isDev if local.dev', () => {
const pluginWithoutDev = { ...localPlugin, dev: false };
const pluginWithDev = { ...localPlugin, dev: true };
expect(mapLocalToCatalog(pluginWithoutDev).isDev).toBe(false);
expect(mapLocalToCatalog(pluginWithDev).isDev).toBe(true);
});
});
describe('mapToCatalogPlugin()', () => {
test('merges local and remote plugin data correctly', () => {
expect(mapToCatalogPlugin(localPlugin, remotePlugin)).toEqual({
description: 'Zabbix plugin for Grafana',
downloads: 33645089,
hasUpdate: false,
id: 'alexanderzobnin-zabbix-app',
info: {
logos: {
small: 'https://grafana.com/api/plugins/alexanderzobnin-zabbix-app/versions/4.1.5/logos/small',
large: 'https://grafana.com/api/plugins/alexanderzobnin-zabbix-app/versions/4.1.5/logos/large',
},
},
error: undefined,
isCore: false,
isDev: false,
isDisabled: false,
isEnterprise: false,
isInstalled: true,
isPublished: true,
name: 'Zabbix',
orgName: 'Alexander Zobnin',
popularity: 0.2111,
publishedAt: '2016-04-06T20:23:41.000Z',
signature: 'valid',
signatureOrg: 'Alexander Zobnin',
signatureType: 'community',
type: 'app',
updatedAt: '2021-05-18T14:53:01.000Z',
Plugins Catalog: Install and show the latest compatible version of a plugin (#41003) * fix(catalog): prefer rendering installed version over latest version * feat(catalog): signify installed version in version history * feat(catalog): introduce installedVersion and latestVersion * refactor(catalog): use latestVersion for installation, simplify plugindetails header logic * refactor(catalog): clean up installedVersion and latestVersion * feat(catalog): use table-layout so versions list table has consistent column widths * test(catalog): update failing tests * removed the need of having a latest version in the plugin catalog type root level. * fixed flaky test depending on what locale it was being running with. * added missing test to verify version for a remote plugin. * fixed version in header. * preventing the UI from break if no versions are available. * fixed failing test due to missing mock data. * added todo as a reminder. * refactor(catalog): prefer grafana plugin icons over gcom notfound images * refactor(Plugins/Admin): change constant name * refactor(Plugins/Admin): add comment to make condition easier to understand * chore: update go modules * feat(Backend/Plugins): add "dependencies" field to `PluginListItem` * feat(Plugins/Admin): show the grafana dependency for the installed version * refactor(Plugins/Admin): use the local version of links * refactor(Plugins/Admin): prefer the local version for `.type` * refactor(Plugins/ADmin): prefer the local `.description` field * fix(Plugins/Admin): fix tests * test(plugins/api): update the expected response for the `api/plugins` tests * chore(Plugins/Admin): add todo comments to check preferation of remote/local values * feat(backend/api): always send the grafana version as a header when proxying to GCOM * feat(plugins/admin): use the `isCompatible` flag to get the latest compatible version * feat(plugins/admin): show the latest compatible version in the versions list * fix(plugins/admin): show the grafana dependency for the latest compatible version * fix(plugins/admin): update the version list when installing/uninstalling a plugin * test(plugins/admin): add some test-cases for the latest-compatible-version * fix(plugins/admin): show the grafana dependency for the installed version (if installed) * feat(plugins/backend): add the `dependencies.grafanaDependency` property to the plugin object * test(plugins/backend): fix tests by adjusting expected response json Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com> Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
4 years ago
installedVersion: '4.2.2',
});
});
Plugins Catalog: Install and show the latest compatible version of a plugin (#41003) * fix(catalog): prefer rendering installed version over latest version * feat(catalog): signify installed version in version history * feat(catalog): introduce installedVersion and latestVersion * refactor(catalog): use latestVersion for installation, simplify plugindetails header logic * refactor(catalog): clean up installedVersion and latestVersion * feat(catalog): use table-layout so versions list table has consistent column widths * test(catalog): update failing tests * removed the need of having a latest version in the plugin catalog type root level. * fixed flaky test depending on what locale it was being running with. * added missing test to verify version for a remote plugin. * fixed version in header. * preventing the UI from break if no versions are available. * fixed failing test due to missing mock data. * added todo as a reminder. * refactor(catalog): prefer grafana plugin icons over gcom notfound images * refactor(Plugins/Admin): change constant name * refactor(Plugins/Admin): add comment to make condition easier to understand * chore: update go modules * feat(Backend/Plugins): add "dependencies" field to `PluginListItem` * feat(Plugins/Admin): show the grafana dependency for the installed version * refactor(Plugins/Admin): use the local version of links * refactor(Plugins/Admin): prefer the local version for `.type` * refactor(Plugins/ADmin): prefer the local `.description` field * fix(Plugins/Admin): fix tests * test(plugins/api): update the expected response for the `api/plugins` tests * chore(Plugins/Admin): add todo comments to check preferation of remote/local values * feat(backend/api): always send the grafana version as a header when proxying to GCOM * feat(plugins/admin): use the `isCompatible` flag to get the latest compatible version * feat(plugins/admin): show the latest compatible version in the versions list * fix(plugins/admin): show the grafana dependency for the latest compatible version * fix(plugins/admin): update the version list when installing/uninstalling a plugin * test(plugins/admin): add some test-cases for the latest-compatible-version * fix(plugins/admin): show the grafana dependency for the installed version (if installed) * feat(plugins/backend): add the `dependencies.grafanaDependency` property to the plugin object * test(plugins/backend): fix tests by adjusting expected response json Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com> Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
4 years ago
test('`.description` - prefers the local', () => {
// Local & Remote
expect(
mapToCatalogPlugin(
{ ...localPlugin, info: { ...localPlugin.info, description: 'Local description' } },
{ ...remotePlugin, description: 'Remote description' }
)
Plugins Catalog: Install and show the latest compatible version of a plugin (#41003) * fix(catalog): prefer rendering installed version over latest version * feat(catalog): signify installed version in version history * feat(catalog): introduce installedVersion and latestVersion * refactor(catalog): use latestVersion for installation, simplify plugindetails header logic * refactor(catalog): clean up installedVersion and latestVersion * feat(catalog): use table-layout so versions list table has consistent column widths * test(catalog): update failing tests * removed the need of having a latest version in the plugin catalog type root level. * fixed flaky test depending on what locale it was being running with. * added missing test to verify version for a remote plugin. * fixed version in header. * preventing the UI from break if no versions are available. * fixed failing test due to missing mock data. * added todo as a reminder. * refactor(catalog): prefer grafana plugin icons over gcom notfound images * refactor(Plugins/Admin): change constant name * refactor(Plugins/Admin): add comment to make condition easier to understand * chore: update go modules * feat(Backend/Plugins): add "dependencies" field to `PluginListItem` * feat(Plugins/Admin): show the grafana dependency for the installed version * refactor(Plugins/Admin): use the local version of links * refactor(Plugins/Admin): prefer the local version for `.type` * refactor(Plugins/ADmin): prefer the local `.description` field * fix(Plugins/Admin): fix tests * test(plugins/api): update the expected response for the `api/plugins` tests * chore(Plugins/Admin): add todo comments to check preferation of remote/local values * feat(backend/api): always send the grafana version as a header when proxying to GCOM * feat(plugins/admin): use the `isCompatible` flag to get the latest compatible version * feat(plugins/admin): show the latest compatible version in the versions list * fix(plugins/admin): show the grafana dependency for the latest compatible version * fix(plugins/admin): update the version list when installing/uninstalling a plugin * test(plugins/admin): add some test-cases for the latest-compatible-version * fix(plugins/admin): show the grafana dependency for the installed version (if installed) * feat(plugins/backend): add the `dependencies.grafanaDependency` property to the plugin object * test(plugins/backend): fix tests by adjusting expected response json Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com> Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
4 years ago
).toMatchObject({ description: 'Local description' });
// Remote only
expect(mapToCatalogPlugin(undefined, { ...remotePlugin, description: 'Remote description' })).toMatchObject({
description: 'Remote description',
});
// Local only
expect(
mapToCatalogPlugin({ ...localPlugin, info: { ...localPlugin.info, description: 'Local description' } })
).toMatchObject({ description: 'Local description' });
// No local or remote
expect(mapToCatalogPlugin()).toMatchObject({ description: '' });
});
test('`.hasUpdate` - prefers the local', () => {
// Local only
expect(mapToCatalogPlugin({ ...localPlugin })).toMatchObject({ hasUpdate: false });
expect(mapToCatalogPlugin({ ...localPlugin, hasUpdate: true })).toMatchObject({ hasUpdate: true });
// No local or remote
expect(mapToCatalogPlugin()).toMatchObject({ hasUpdate: false });
});
test('`.downloads` - relies on the remote', () => {
// Local & Remote
expect(mapToCatalogPlugin(localPlugin, { ...remotePlugin, downloads: 99 })).toMatchObject({ downloads: 99 });
// Remote only
expect(mapToCatalogPlugin(undefined, { ...remotePlugin, downloads: 99 })).toMatchObject({ downloads: 99 });
// Local only
expect(mapToCatalogPlugin(localPlugin)).toMatchObject({ downloads: 0 });
// No local or remote
expect(mapToCatalogPlugin()).toMatchObject({ downloads: 0 });
});
test('`.isCore` - prefers the remote', () => {
// Local & Remote
expect(mapToCatalogPlugin(localPlugin, { ...remotePlugin, internal: true })).toMatchObject({ isCore: true });
// Remote only
expect(mapToCatalogPlugin(undefined, { ...remotePlugin, internal: true })).toMatchObject({ isCore: true });
expect(mapToCatalogPlugin(undefined, { ...remotePlugin, internal: false })).toMatchObject({ isCore: false });
// Local only
expect(mapToCatalogPlugin({ ...localPlugin, signature: PluginSignatureStatus.internal })).toMatchObject({
isCore: true,
});
expect(mapToCatalogPlugin({ ...localPlugin, signature: PluginSignatureStatus.valid })).toMatchObject({
isCore: false,
});
// No local or remote
expect(mapToCatalogPlugin()).toMatchObject({ isCore: false });
});
test('`.isDev` - prefers the local', () => {
// Local & Remote
expect(mapToCatalogPlugin({ ...localPlugin, dev: true }, remotePlugin)).toMatchObject({ isDev: true });
// Remote only
expect(mapToCatalogPlugin(undefined, remotePlugin)).toMatchObject({ isDev: false });
// Local only
expect(mapToCatalogPlugin({ ...localPlugin, dev: true }, undefined)).toMatchObject({ isDev: true });
expect(mapToCatalogPlugin({ ...localPlugin, dev: undefined }, undefined)).toMatchObject({ isDev: false });
// No local or remote
expect(mapToCatalogPlugin()).toMatchObject({ isDev: false });
});
test('`.isEnterprise` - prefers the remote', () => {
// Local & Remote
expect(mapToCatalogPlugin(localPlugin, { ...remotePlugin, status: 'enterprise' })).toMatchObject({
isEnterprise: true,
});
expect(mapToCatalogPlugin(localPlugin, { ...remotePlugin, status: 'unknown' })).toMatchObject({
isEnterprise: false,
});
// Remote only
expect(mapToCatalogPlugin(undefined, { ...remotePlugin, status: 'enterprise' })).toMatchObject({
isEnterprise: true,
});
// Local only
expect(mapToCatalogPlugin(localPlugin)).toMatchObject({ isEnterprise: false });
// No local or remote
expect(mapToCatalogPlugin()).toMatchObject({ isEnterprise: false });
});
test('`.isInstalled` - prefers the local', () => {
// Local & Remote
expect(mapToCatalogPlugin(localPlugin, remotePlugin)).toMatchObject({ isInstalled: true });
// Remote only
expect(mapToCatalogPlugin(undefined, remotePlugin)).toMatchObject({ isInstalled: false });
// Local only
expect(mapToCatalogPlugin(localPlugin, undefined)).toMatchObject({ isInstalled: true });
// No local or remote
expect(mapToCatalogPlugin()).toMatchObject({ isInstalled: false });
});
test('`.name` - prefers the remote', () => {
// Local & Remote
expect(
mapToCatalogPlugin({ ...localPlugin, name: 'Local name' }, { ...remotePlugin, name: 'Remote name' })
).toMatchObject({ name: 'Remote name' });
// Remote only
expect(mapToCatalogPlugin(undefined, { ...remotePlugin, name: 'Remote name' })).toMatchObject({
name: 'Remote name',
});
// Local only
expect(mapToCatalogPlugin({ ...localPlugin, name: 'Local name' })).toMatchObject({ name: 'Local name' });
// No local or remote
expect(mapToCatalogPlugin()).toMatchObject({ name: '' });
});
test('`.orgName` - prefers the remote', () => {
// Local & Remote
expect(mapToCatalogPlugin(localPlugin, { ...remotePlugin, orgName: 'Remote org' })).toMatchObject({
orgName: 'Remote org',
});
// Remote only
expect(mapToCatalogPlugin(undefined, { ...remotePlugin, orgName: 'Remote org' })).toMatchObject({
orgName: 'Remote org',
});
// Local only
expect(mapToCatalogPlugin(localPlugin)).toMatchObject({ orgName: 'Alexander Zobnin' });
// No local or remote
expect(mapToCatalogPlugin()).toMatchObject({ orgName: '' });
});
test('`.popularity` - prefers the remote', () => {
// Local & Remote
expect(mapToCatalogPlugin(localPlugin, { ...remotePlugin, popularity: 10 })).toMatchObject({ popularity: 10 });
// Remote only
expect(mapToCatalogPlugin(undefined, { ...remotePlugin, popularity: 10 })).toMatchObject({ popularity: 10 });
// Local only
expect(mapToCatalogPlugin(localPlugin)).toMatchObject({ popularity: 0 });
// No local or remote
expect(mapToCatalogPlugin()).toMatchObject({ popularity: 0 });
});
test('`.publishedAt` - prefers the remote', () => {
// Local & Remote
expect(mapToCatalogPlugin(localPlugin, { ...remotePlugin, createdAt: '2020-01-01' })).toMatchObject({
publishedAt: '2020-01-01',
});
// Remote only
expect(mapToCatalogPlugin(undefined, { ...remotePlugin, createdAt: '2020-01-01' })).toMatchObject({
publishedAt: '2020-01-01',
});
// Local only
expect(mapToCatalogPlugin(localPlugin)).toMatchObject({ publishedAt: '' });
// No local or remote
expect(mapToCatalogPlugin()).toMatchObject({ publishedAt: '' });
});
Plugins Catalog: Install and show the latest compatible version of a plugin (#41003) * fix(catalog): prefer rendering installed version over latest version * feat(catalog): signify installed version in version history * feat(catalog): introduce installedVersion and latestVersion * refactor(catalog): use latestVersion for installation, simplify plugindetails header logic * refactor(catalog): clean up installedVersion and latestVersion * feat(catalog): use table-layout so versions list table has consistent column widths * test(catalog): update failing tests * removed the need of having a latest version in the plugin catalog type root level. * fixed flaky test depending on what locale it was being running with. * added missing test to verify version for a remote plugin. * fixed version in header. * preventing the UI from break if no versions are available. * fixed failing test due to missing mock data. * added todo as a reminder. * refactor(catalog): prefer grafana plugin icons over gcom notfound images * refactor(Plugins/Admin): change constant name * refactor(Plugins/Admin): add comment to make condition easier to understand * chore: update go modules * feat(Backend/Plugins): add "dependencies" field to `PluginListItem` * feat(Plugins/Admin): show the grafana dependency for the installed version * refactor(Plugins/Admin): use the local version of links * refactor(Plugins/Admin): prefer the local version for `.type` * refactor(Plugins/ADmin): prefer the local `.description` field * fix(Plugins/Admin): fix tests * test(plugins/api): update the expected response for the `api/plugins` tests * chore(Plugins/Admin): add todo comments to check preferation of remote/local values * feat(backend/api): always send the grafana version as a header when proxying to GCOM * feat(plugins/admin): use the `isCompatible` flag to get the latest compatible version * feat(plugins/admin): show the latest compatible version in the versions list * fix(plugins/admin): show the grafana dependency for the latest compatible version * fix(plugins/admin): update the version list when installing/uninstalling a plugin * test(plugins/admin): add some test-cases for the latest-compatible-version * fix(plugins/admin): show the grafana dependency for the installed version (if installed) * feat(plugins/backend): add the `dependencies.grafanaDependency` property to the plugin object * test(plugins/backend): fix tests by adjusting expected response json Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com> Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
4 years ago
test('`.type` - prefers the local', () => {
// Local & Remote
expect(
mapToCatalogPlugin(
{ ...localPlugin, type: PluginType.app },
{ ...remotePlugin, typeCode: PluginType.datasource }
)
).toMatchObject({
Plugins Catalog: Install and show the latest compatible version of a plugin (#41003) * fix(catalog): prefer rendering installed version over latest version * feat(catalog): signify installed version in version history * feat(catalog): introduce installedVersion and latestVersion * refactor(catalog): use latestVersion for installation, simplify plugindetails header logic * refactor(catalog): clean up installedVersion and latestVersion * feat(catalog): use table-layout so versions list table has consistent column widths * test(catalog): update failing tests * removed the need of having a latest version in the plugin catalog type root level. * fixed flaky test depending on what locale it was being running with. * added missing test to verify version for a remote plugin. * fixed version in header. * preventing the UI from break if no versions are available. * fixed failing test due to missing mock data. * added todo as a reminder. * refactor(catalog): prefer grafana plugin icons over gcom notfound images * refactor(Plugins/Admin): change constant name * refactor(Plugins/Admin): add comment to make condition easier to understand * chore: update go modules * feat(Backend/Plugins): add "dependencies" field to `PluginListItem` * feat(Plugins/Admin): show the grafana dependency for the installed version * refactor(Plugins/Admin): use the local version of links * refactor(Plugins/Admin): prefer the local version for `.type` * refactor(Plugins/ADmin): prefer the local `.description` field * fix(Plugins/Admin): fix tests * test(plugins/api): update the expected response for the `api/plugins` tests * chore(Plugins/Admin): add todo comments to check preferation of remote/local values * feat(backend/api): always send the grafana version as a header when proxying to GCOM * feat(plugins/admin): use the `isCompatible` flag to get the latest compatible version * feat(plugins/admin): show the latest compatible version in the versions list * fix(plugins/admin): show the grafana dependency for the latest compatible version * fix(plugins/admin): update the version list when installing/uninstalling a plugin * test(plugins/admin): add some test-cases for the latest-compatible-version * fix(plugins/admin): show the grafana dependency for the installed version (if installed) * feat(plugins/backend): add the `dependencies.grafanaDependency` property to the plugin object * test(plugins/backend): fix tests by adjusting expected response json Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com> Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
4 years ago
type: PluginType.app,
});
// Remote only
expect(mapToCatalogPlugin(undefined, { ...remotePlugin, typeCode: PluginType.datasource })).toMatchObject({
type: PluginType.datasource,
});
// Local only
expect(mapToCatalogPlugin({ ...localPlugin, type: PluginType.app })).toMatchObject({
type: PluginType.app,
});
// No local or remote
expect(mapToCatalogPlugin()).toMatchObject({ type: undefined });
});
test('`.signature` - prefers the local', () => {
// Local & Remote
expect(
mapToCatalogPlugin(
{ ...localPlugin, signature: PluginSignatureStatus.valid },
{ ...remotePlugin, signatureType: '', versionSignatureType: '' }
)
).toMatchObject({
signature: PluginSignatureStatus.valid,
});
expect(
mapToCatalogPlugin(
{ ...localPlugin, signature: PluginSignatureStatus.missing },
{
...remotePlugin,
signatureType: PluginSignatureType.grafana,
versionSignatureType: PluginSignatureType.grafana,
}
)
).toMatchObject({
signature: PluginSignatureStatus.missing,
});
// Remote only
expect(
mapToCatalogPlugin(undefined, { ...remotePlugin, signatureType: PluginSignatureType.grafana })
).toMatchObject({
signature: PluginSignatureStatus.valid,
});
expect(
mapToCatalogPlugin(undefined, { ...remotePlugin, versionSignatureType: PluginSignatureType.grafana })
).toMatchObject({
signature: PluginSignatureStatus.valid,
});
expect(
mapToCatalogPlugin(undefined, { ...remotePlugin, signatureType: '', versionSignatureType: '' })
).toMatchObject({
signature: PluginSignatureStatus.missing,
});
// Local only
expect(mapToCatalogPlugin({ ...localPlugin, signature: PluginSignatureStatus.valid })).toMatchObject({
signature: PluginSignatureStatus.valid,
});
// No local or remote
expect(mapToCatalogPlugin()).toMatchObject({ signature: PluginSignatureStatus.missing });
});
test('`.signatureOrg` - prefers the local', () => {
// Local & Remote
expect(
mapToCatalogPlugin(
{ ...localPlugin, signatureOrg: 'Local Org' },
{ ...remotePlugin, versionSignedByOrgName: 'Remote Org' }
)
).toMatchObject({
signatureOrg: 'Local Org',
});
// Remote only
expect(mapToCatalogPlugin(undefined, { ...remotePlugin, versionSignedByOrgName: 'Remote Org' })).toMatchObject({
signatureOrg: 'Remote Org',
});
// Local only
expect(mapToCatalogPlugin({ ...localPlugin, signatureOrg: 'Local Org' })).toMatchObject({
signatureOrg: 'Local Org',
});
// No local or remote
expect(mapToCatalogPlugin()).toMatchObject({ signatureOrg: undefined });
});
test('`.signatureType` - prefers the local', () => {
// Local & Remote
expect(
mapToCatalogPlugin(
{ ...localPlugin, signatureType: PluginSignatureType.core },
{ ...remotePlugin, signatureType: PluginSignatureType.grafana }
)
).toMatchObject({
signatureType: PluginSignatureType.core,
});
// Remote only
expect(
mapToCatalogPlugin(undefined, {
...remotePlugin,
versionSignatureType: PluginSignatureType.core,
signatureType: PluginSignatureType.grafana,
})
).toMatchObject({
signatureType: PluginSignatureType.core,
});
expect(
mapToCatalogPlugin(undefined, {
...remotePlugin,
versionSignatureType: '',
signatureType: PluginSignatureType.grafana,
})
).toMatchObject({
signatureType: PluginSignatureType.grafana,
});
// Local only
expect(mapToCatalogPlugin({ ...localPlugin, signatureType: PluginSignatureType.core })).toMatchObject({
signatureType: PluginSignatureType.core,
});
// No local or remote
expect(mapToCatalogPlugin()).toMatchObject({ signatureType: undefined });
});
test('`.updatedAt` - prefers the remote', () => {
// Local & Remote
expect(
mapToCatalogPlugin(
{ ...localPlugin, info: { ...localPlugin.info, updated: '2019-01-01' } },
{ ...remotePlugin, updatedAt: '2020-01-01' }
)
).toMatchObject({
updatedAt: '2020-01-01',
});
// Remote only
expect(mapToCatalogPlugin(undefined, { ...remotePlugin, updatedAt: '2020-01-01' })).toMatchObject({
updatedAt: '2020-01-01',
});
// Local only
expect(
mapToCatalogPlugin({ ...localPlugin, info: { ...localPlugin.info, updated: '2019-01-01' } })
).toMatchObject({
updatedAt: '2019-01-01',
});
// No local or remote
expect(mapToCatalogPlugin()).toMatchObject({ updatedAt: '' });
});
});
describe('sortPlugins()', () => {
test('should be possible to sort by `name` ASC', () => {
const sorted = sortPlugins(
[
getCatalogPluginMock({ id: 'zabbix', name: 'Zabbix' }),
getCatalogPluginMock({ id: 'snowflake', name: 'Snowflake' }),
getCatalogPluginMock({ id: 'jira', name: 'Jira' }),
getCatalogPluginMock({ id: 'pie-chart', name: 'Pie Chart' }),
getCatalogPluginMock({ id: 'cloud-watch', name: 'CloudWatch' }),
],
Sorters.nameAsc
);
expect(sorted.map(({ name }) => name)).toEqual(['CloudWatch', 'Jira', 'Pie Chart', 'Snowflake', 'Zabbix']);
});
test('should be possible to sort by `name` DESC', () => {
const sorted = sortPlugins(
[
getCatalogPluginMock({ id: 'zabbix', name: 'Zabbix' }),
getCatalogPluginMock({ id: 'snowflake', name: 'Snowflake' }),
getCatalogPluginMock({ id: 'jira', name: 'Jira' }),
getCatalogPluginMock({ id: 'pie-chart', name: 'Pie Chart' }),
getCatalogPluginMock({ id: 'cloud-watch', name: 'CloudWatch' }),
],
Sorters.nameDesc
);
expect(sorted.map(({ name }) => name)).toEqual(['Zabbix', 'Snowflake', 'Pie Chart', 'Jira', 'CloudWatch']);
});
test('should be possible to sort by `updated` (latest first)', () => {
const sorted = sortPlugins(
[
getCatalogPluginMock({ id: 'zabbix', updatedAt: '2010-01-01' }),
getCatalogPluginMock({ id: 'snowflake', updatedAt: '2012-01-01' }),
getCatalogPluginMock({ id: 'jira', updatedAt: '2005-01-01' }),
getCatalogPluginMock({ id: 'pie-chart', updatedAt: '2021-01-01' }),
getCatalogPluginMock({ id: 'cloud-watch', updatedAt: '2009-01-01' }),
],
Sorters.updated
);
expect(sorted.map(({ id }) => id)).toEqual(['pie-chart', 'snowflake', 'zabbix', 'cloud-watch', 'jira']);
});
test('should be possible to sort by `published` (latest first)', () => {
const sorted = sortPlugins(
[
getCatalogPluginMock({ id: 'zabbix', publishedAt: '2010-01-01' }),
getCatalogPluginMock({ id: 'snowflake', publishedAt: '2012-01-01' }),
getCatalogPluginMock({ id: 'jira', publishedAt: '2005-01-01' }),
getCatalogPluginMock({ id: 'pie-chart', publishedAt: '2021-01-01' }),
getCatalogPluginMock({ id: 'cloud-watch', publishedAt: '2009-01-01' }),
],
Sorters.published
);
expect(sorted.map(({ id }) => id)).toEqual(['pie-chart', 'snowflake', 'zabbix', 'cloud-watch', 'jira']);
});
test('should be possible to sort by `downloads` (greatest first)', () => {
const sorted = sortPlugins(
[
getCatalogPluginMock({ id: 'zabbix', downloads: 30 }),
getCatalogPluginMock({ id: 'snowflake', downloads: 10 }),
getCatalogPluginMock({ id: 'jira', downloads: 100 }),
getCatalogPluginMock({ id: 'pie-chart', downloads: 350 }),
getCatalogPluginMock({ id: 'cloud-watch', downloads: 200 }),
],
Sorters.downloads
);
expect(sorted.map(({ id }) => id)).toEqual(['pie-chart', 'cloud-watch', 'jira', 'zabbix', 'snowflake']);
});
});
describe('isLocalPluginVisible()', () => {
test('should return TRUE if the plugin is not listed as hidden in the main Grafana configuration', () => {
config.pluginCatalogHiddenPlugins = ['akumuli-datasource'];
const plugin = getLocalPluginMock({
id: 'barchart',
});
expect(isLocalPluginVisible(plugin)).toBe(true);
});
test('should return FALSE if the plugin is listed as hidden in the main Grafana configuration', () => {
config.pluginCatalogHiddenPlugins = ['akumuli-datasource'];
const plugin = getLocalPluginMock({
id: 'akumuli-datasource',
});
expect(isLocalPluginVisible(plugin)).toBe(false);
});
});
describe('isRemotePluginVisible()', () => {
test('should return TRUE if the plugin is not listed as hidden in the main Grafana configuration', () => {
config.pluginCatalogHiddenPlugins = ['akumuli-datasource'];
const plugin = getRemotePluginMock({
slug: 'barchart',
});
expect(isRemotePluginVisible(plugin)).toBe(true);
});
test('should return FALSE if the plugin is listed as hidden in the main Grafana configuration', () => {
config.pluginCatalogHiddenPlugins = ['akumuli-datasource'];
const plugin = getRemotePluginMock({
slug: 'akumuli-datasource',
});
expect(isRemotePluginVisible(plugin)).toBe(false);
});
});
});