mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
switch to using featureEnabled for enterprise features (#41559)
* switch to using featureEnabled for enterprise features
This commit is contained in:
parent
9eb82f9fff
commit
34f757ba5a
@ -13,12 +13,6 @@ import { MapLayerOptions } from '../geo/layer';
|
||||
export interface BuildInfo {
|
||||
version: string;
|
||||
commit: string;
|
||||
/**
|
||||
* Is set to true when running Grafana Enterprise edition.
|
||||
*
|
||||
* @deprecated use `licenseInfo.hasLicense` instead
|
||||
*/
|
||||
isEnterprise: boolean;
|
||||
env: string;
|
||||
edition: GrafanaEdition;
|
||||
latestVersion: string;
|
||||
@ -62,12 +56,11 @@ export interface FeatureToggles {
|
||||
* @public
|
||||
*/
|
||||
export interface LicenseInfo {
|
||||
hasLicense: boolean;
|
||||
expiry: number;
|
||||
licenseUrl: string;
|
||||
stateInfo: string;
|
||||
hasValidLicense: boolean;
|
||||
edition: GrafanaEdition;
|
||||
enabledFeatures: { [key: string]: boolean };
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,7 +124,6 @@ export class GrafanaBootConfig implements GrafanaConfig {
|
||||
version: 'v1.0',
|
||||
commit: '1',
|
||||
env: 'production',
|
||||
isEnterprise: false,
|
||||
},
|
||||
viewersCanEdit: false,
|
||||
editorsCanAdmin: false,
|
||||
|
@ -8,6 +8,7 @@ export * from './config';
|
||||
export * from './types';
|
||||
export { loadPluginCss, SystemJS, PluginCssOptions } from './utils/plugin';
|
||||
export { reportMetaAnalytics, reportInteraction, reportPageview } from './utils/analytics';
|
||||
export { featureEnabled } from './utils/licensing';
|
||||
export { logInfo, logDebug, logWarning, logError } from './utils/logging';
|
||||
export {
|
||||
DataSourceWithBackend,
|
||||
|
6
packages/grafana-runtime/src/utils/licensing.ts
Normal file
6
packages/grafana-runtime/src/utils/licensing.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { config } from '../config';
|
||||
|
||||
export const featureEnabled = (feature: string): boolean => {
|
||||
const { enabledFeatures } = config.licenseInfo;
|
||||
return enabledFeatures && enabledFeatures[feature];
|
||||
};
|
@ -250,15 +250,13 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
|
||||
"latestVersion": hs.updateChecker.LatestGrafanaVersion(),
|
||||
"hasUpdate": hs.updateChecker.GrafanaUpdateAvailable(),
|
||||
"env": setting.Env,
|
||||
"isEnterprise": hs.License.HasValidLicense(),
|
||||
},
|
||||
"licenseInfo": map[string]interface{}{
|
||||
"hasLicense": hs.License.HasLicense(),
|
||||
"hasValidLicense": hs.License.HasValidLicense(),
|
||||
"expiry": hs.License.Expiry(),
|
||||
"stateInfo": hs.License.StateInfo(),
|
||||
"licenseUrl": hs.License.LicenseURL(hasAccess(accesscontrol.ReqGrafanaAdmin, accesscontrol.LicensingPageReaderAccess)),
|
||||
"edition": hs.License.Edition(),
|
||||
"enabledFeatures": hs.License.EnabledFeatures(),
|
||||
},
|
||||
"featureToggles": hs.Cfg.FeatureToggles,
|
||||
"rendererAvailable": hs.RenderService.IsAvailable(),
|
||||
|
@ -611,7 +611,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
NewGrafanaVersion: hs.updateChecker.LatestGrafanaVersion(),
|
||||
NewGrafanaVersionExists: hs.updateChecker.GrafanaUpdateAvailable(),
|
||||
AppName: setting.ApplicationName,
|
||||
AppNameBodyClass: getAppNameBodyClass(hs.License.HasValidLicense()),
|
||||
AppNameBodyClass: "app-grafana",
|
||||
FavIcon: "public/img/fav32.png",
|
||||
AppleTouchIcon: "public/img/apple-touch-icon.png",
|
||||
AppTitle: "Grafana",
|
||||
@ -680,11 +680,3 @@ func (hs *HTTPServer) NotFoundHandler(c *models.ReqContext) {
|
||||
|
||||
c.HTML(404, "index", data)
|
||||
}
|
||||
|
||||
func getAppNameBodyClass(validLicense bool) string {
|
||||
if validLicense {
|
||||
return "app-enterprise"
|
||||
}
|
||||
|
||||
return "app-grafana"
|
||||
}
|
||||
|
@ -349,7 +349,7 @@ func (hs *HTTPServer) RedirectResponseWithError(ctx *models.ReqContext, err erro
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) samlEnabled() bool {
|
||||
return hs.SettingsProvider.KeyValue("auth.saml", "enabled").MustBool(false) && hs.License.HasValidLicense()
|
||||
return hs.SettingsProvider.KeyValue("auth.saml", "enabled").MustBool(false) && hs.License.FeatureEnabled("saml")
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) samlName() string {
|
||||
|
@ -30,7 +30,7 @@ func (hs *HTTPServer) GetTeamMembers(c *models.ReqContext) response.Response {
|
||||
member.AvatarUrl = dtos.GetGravatarUrl(member.Email)
|
||||
member.Labels = []string{}
|
||||
|
||||
if hs.License.HasValidLicense() && member.External {
|
||||
if hs.License.FeatureEnabled("teamgroupsync") && member.External {
|
||||
authProvider := GetAuthProviderLabel(member.AuthModule)
|
||||
member.Labels = append(member.Labels, authProvider)
|
||||
}
|
||||
|
@ -1,12 +1,6 @@
|
||||
package models
|
||||
|
||||
type Licensing interface {
|
||||
// HasValidLicense is true if a valid license exists
|
||||
HasValidLicense() bool
|
||||
|
||||
// HasLicense is true if there is a license provided
|
||||
HasLicense() bool
|
||||
|
||||
// Expiry returns the unix epoch timestamp when the license expires, or 0 if no valid license is provided
|
||||
Expiry() int64
|
||||
|
||||
@ -19,6 +13,10 @@ type Licensing interface {
|
||||
LicenseURL(showAdminLicensingPage bool) string
|
||||
|
||||
StateInfo() string
|
||||
|
||||
EnabledFeatures() map[string]bool
|
||||
|
||||
FeatureEnabled(feature string) bool
|
||||
}
|
||||
|
||||
type LicenseEnvironment interface {
|
||||
|
@ -196,11 +196,11 @@ func (i *Initializer) envVars(plugin *plugins.Plugin) []string {
|
||||
fmt.Sprintf("GF_VERSION=%s", i.cfg.BuildVersion),
|
||||
}
|
||||
|
||||
if i.license != nil && i.license.HasLicense() {
|
||||
if i.license != nil {
|
||||
hostEnv = append(
|
||||
hostEnv,
|
||||
fmt.Sprintf("GF_EDITION=%s", i.license.Edition()),
|
||||
fmt.Sprintf("GF_ENTERPRISE_license_PATH=%s", i.cfg.EnterpriseLicensePath),
|
||||
fmt.Sprintf("GF_ENTERPRISE_LICENSE_PATH=%s", i.cfg.EnterpriseLicensePath),
|
||||
)
|
||||
|
||||
if envProvider, ok := i.license.(models.LicenseEnvironment); ok {
|
||||
|
@ -227,8 +227,8 @@ func TestInitializer_envVars(t *testing.T) {
|
||||
}
|
||||
|
||||
licensing := &testLicensingService{
|
||||
edition: "test",
|
||||
hasLicense: true,
|
||||
edition: "test",
|
||||
tokenRaw: "token",
|
||||
}
|
||||
|
||||
i := &Initializer{
|
||||
@ -249,8 +249,8 @@ func TestInitializer_envVars(t *testing.T) {
|
||||
assert.Equal(t, "GF_PLUGIN_CUSTOM_ENV_VAR=customVal", envVars[0])
|
||||
assert.Equal(t, "GF_VERSION=", envVars[1])
|
||||
assert.Equal(t, "GF_EDITION=test", envVars[2])
|
||||
assert.Equal(t, "GF_ENTERPRISE_license_PATH=/path/to/ent/license", envVars[3])
|
||||
assert.Equal(t, "GF_ENTERPRISE_LICENSE_TEXT=", envVars[4])
|
||||
assert.Equal(t, "GF_ENTERPRISE_LICENSE_PATH=/path/to/ent/license", envVars[3])
|
||||
assert.Equal(t, "GF_ENTERPRISE_LICENSE_TEXT=token", envVars[4])
|
||||
})
|
||||
}
|
||||
|
||||
@ -314,13 +314,8 @@ func Test_pluginSettings_ToEnv(t *testing.T) {
|
||||
}
|
||||
|
||||
type testLicensingService struct {
|
||||
edition string
|
||||
hasLicense bool
|
||||
tokenRaw string
|
||||
}
|
||||
|
||||
func (t *testLicensingService) HasLicense() bool {
|
||||
return t.hasLicense
|
||||
edition string
|
||||
tokenRaw string
|
||||
}
|
||||
|
||||
func (t *testLicensingService) Expiry() int64 {
|
||||
@ -343,14 +338,18 @@ func (t *testLicensingService) LicenseURL(showAdminLicensingPage bool) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t *testLicensingService) HasValidLicense() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *testLicensingService) Environment() map[string]string {
|
||||
return map[string]string{"GF_ENTERPRISE_LICENSE_TEXT": t.tokenRaw}
|
||||
}
|
||||
|
||||
func (*testLicensingService) EnabledFeatures() map[string]bool {
|
||||
return map[string]bool{}
|
||||
}
|
||||
|
||||
func (*testLicensingService) FeatureEnabled(feature string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type testPlugin struct {
|
||||
backendplugin.Plugin
|
||||
}
|
||||
|
@ -894,13 +894,8 @@ func newLoader(cfg *setting.Cfg) *Loader {
|
||||
}
|
||||
|
||||
type fakeLicensingService struct {
|
||||
edition string
|
||||
hasLicense bool
|
||||
tokenRaw string
|
||||
}
|
||||
|
||||
func (t *fakeLicensingService) HasLicense() bool {
|
||||
return t.hasLicense
|
||||
edition string
|
||||
tokenRaw string
|
||||
}
|
||||
|
||||
func (t *fakeLicensingService) Expiry() int64 {
|
||||
@ -923,14 +918,18 @@ func (t *fakeLicensingService) LicenseURL(_ bool) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t *fakeLicensingService) HasValidLicense() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *fakeLicensingService) Environment() map[string]string {
|
||||
return map[string]string{"GF_ENTERPRISE_LICENSE_TEXT": t.tokenRaw}
|
||||
}
|
||||
|
||||
func (*fakeLicensingService) EnabledFeatures() map[string]bool {
|
||||
return map[string]bool{}
|
||||
}
|
||||
|
||||
func (*fakeLicensingService) FeatureEnabled(feature string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type fakeLogger struct {
|
||||
log.Logger
|
||||
}
|
||||
|
@ -16,10 +16,6 @@ type OSSLicensingService struct {
|
||||
HooksService *hooks.HooksService
|
||||
}
|
||||
|
||||
func (*OSSLicensingService) HasLicense() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (*OSSLicensingService) Expiry() int64 {
|
||||
return 0
|
||||
}
|
||||
@ -44,7 +40,11 @@ func (l *OSSLicensingService) LicenseURL(showAdminLicensingPage bool) string {
|
||||
return "https://grafana.com/oss/grafana?utm_source=grafana_footer"
|
||||
}
|
||||
|
||||
func (*OSSLicensingService) HasValidLicense() bool {
|
||||
func (*OSSLicensingService) EnabledFeatures() map[string]bool {
|
||||
return map[string]bool{}
|
||||
}
|
||||
|
||||
func (*OSSLicensingService) FeatureEnabled(feature string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { FC } from 'react';
|
||||
import config from 'app/core/config';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { Icon, IconName } from '@grafana/ui';
|
||||
|
||||
export interface FooterLink {
|
||||
|
@ -10,25 +10,22 @@ jest.mock('@grafana/runtime', () => ({
|
||||
getBackendSrv: () => ({
|
||||
post: postMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('app/core/config', () => {
|
||||
return {
|
||||
config: {
|
||||
loginError: false,
|
||||
buildInfo: {
|
||||
version: 'v1.0',
|
||||
commit: '1',
|
||||
env: 'production',
|
||||
edition: 'Open Source',
|
||||
isEnterprise: false,
|
||||
},
|
||||
licenseInfo: {
|
||||
stateInfo: '',
|
||||
licenseUrl: '',
|
||||
},
|
||||
appSubUrl: '',
|
||||
};
|
||||
});
|
||||
},
|
||||
}));
|
||||
|
||||
const props: Props = {
|
||||
...getRouteComponentProps({
|
||||
queryParams: { code: 'some code' },
|
||||
|
@ -9,24 +9,20 @@ jest.mock('@grafana/runtime', () => ({
|
||||
getBackendSrv: () => ({
|
||||
post: postMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('app/core/config', () => {
|
||||
return {
|
||||
config: {
|
||||
buildInfo: {
|
||||
version: 'v1.0',
|
||||
commit: '1',
|
||||
env: 'production',
|
||||
edition: 'Open Source',
|
||||
isEnterprise: false,
|
||||
},
|
||||
licenseInfo: {
|
||||
stateInfo: '',
|
||||
licenseUrl: '',
|
||||
},
|
||||
appSubUrl: '',
|
||||
};
|
||||
});
|
||||
},
|
||||
}));
|
||||
|
||||
describe('VerifyEmail Page', () => {
|
||||
it('renders correctly', () => {
|
||||
|
@ -8,29 +8,22 @@ jest.mock('@grafana/runtime', () => ({
|
||||
getBackendSrv: () => ({
|
||||
post: postMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('app/core/config', () => {
|
||||
return {
|
||||
config: {
|
||||
loginError: false,
|
||||
buildInfo: {
|
||||
version: 'v1.0',
|
||||
commit: '1',
|
||||
env: 'production',
|
||||
edition: 'Open Source',
|
||||
isEnterprise: false,
|
||||
},
|
||||
licenseInfo: {
|
||||
stateInfo: '',
|
||||
licenseUrl: '',
|
||||
},
|
||||
appSubUrl: '',
|
||||
getConfig: () => ({
|
||||
appSubUrl: '',
|
||||
verifyEmailEnabled: false,
|
||||
}),
|
||||
};
|
||||
});
|
||||
verifyEmailEnabled: false,
|
||||
},
|
||||
}));
|
||||
|
||||
describe('Login Page', () => {
|
||||
it('renders correctly', () => {
|
||||
|
@ -10,30 +10,23 @@ jest.mock('@grafana/runtime', () => ({
|
||||
getBackendSrv: () => ({
|
||||
post: postMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('app/core/config', () => {
|
||||
return {
|
||||
config: {
|
||||
loginError: false,
|
||||
buildInfo: {
|
||||
version: 'v1.0',
|
||||
commit: '1',
|
||||
env: 'production',
|
||||
edition: 'Open Source',
|
||||
isEnterprise: false,
|
||||
},
|
||||
licenseInfo: {
|
||||
stateInfo: '',
|
||||
licenseUrl: '',
|
||||
},
|
||||
appSubUrl: '',
|
||||
getConfig: () => ({
|
||||
autoAssignOrg: false,
|
||||
verifyEmailEnabled: true,
|
||||
appSubUrl: '',
|
||||
}),
|
||||
};
|
||||
});
|
||||
autoAssignOrg: false,
|
||||
verifyEmailEnabled: true,
|
||||
},
|
||||
}));
|
||||
|
||||
const props = {
|
||||
email: '',
|
||||
|
@ -9,27 +9,21 @@ jest.mock('@grafana/runtime', () => ({
|
||||
getBackendSrv: () => ({
|
||||
post: postMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('app/core/config', () => {
|
||||
return {
|
||||
config: {
|
||||
buildInfo: {
|
||||
version: 'v1.0',
|
||||
commit: '1',
|
||||
env: 'production',
|
||||
edition: 'Open Source',
|
||||
isEnterprise: false,
|
||||
},
|
||||
licenseInfo: {
|
||||
stateInfo: '',
|
||||
licenseUrl: '',
|
||||
},
|
||||
getConfig: () => ({
|
||||
verifyEmailEnabled: true,
|
||||
appSubUrl: '',
|
||||
}),
|
||||
};
|
||||
});
|
||||
verifyEmailEnabled: true,
|
||||
appSubUrl: '',
|
||||
},
|
||||
}));
|
||||
|
||||
describe('VerifyEmail Page', () => {
|
||||
it('renders correctly', () => {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import config from '../../core/config';
|
||||
import { extend } from 'lodash';
|
||||
import { rangeUtil, WithAccessControlMetadata } from '@grafana/data';
|
||||
import { featureEnabled } from '@grafana/runtime';
|
||||
import { AccessControlAction, UserPermission } from 'app/types';
|
||||
|
||||
export class User {
|
||||
@ -82,7 +83,7 @@ export class ContextSrv {
|
||||
}
|
||||
|
||||
accessControlEnabled(): boolean {
|
||||
return config.licenseInfo.hasLicense && config.featureToggles['accesscontrol'];
|
||||
return featureEnabled('accesscontrol') && config.featureToggles['accesscontrol'];
|
||||
}
|
||||
|
||||
// Checks whether user has required permission
|
||||
|
@ -21,7 +21,6 @@ describe('SentryEchoBackend', () => {
|
||||
const buildInfo: BuildInfo = {
|
||||
version: '1.0',
|
||||
commit: 'abcd123',
|
||||
isEnterprise: false,
|
||||
env: 'production',
|
||||
edition: GrafanaEdition.OpenSource,
|
||||
latestVersion: 'ba',
|
||||
|
@ -10,6 +10,9 @@ jest.mock('@grafana/runtime', () => ({
|
||||
get: jest.fn().mockResolvedValue([]),
|
||||
post: postMock,
|
||||
}),
|
||||
config: {
|
||||
appSubUrl: '/subUrl',
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('app/core/services/context_srv', () => ({
|
||||
@ -18,12 +21,6 @@ jest.mock('app/core/services/context_srv', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('app/core/config', () => {
|
||||
return {
|
||||
appSubUrl: '/subUrl',
|
||||
};
|
||||
});
|
||||
|
||||
let wrapper;
|
||||
let orgSwitcher: OrgSwitcher;
|
||||
|
||||
|
@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
import { NavModel } from '@grafana/data';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import config from 'app/core/config';
|
||||
import { featureEnabled } from '@grafana/runtime';
|
||||
import Page from 'app/core/components/Page/Page';
|
||||
import { UserProfile } from './UserProfile';
|
||||
import { UserPermissions } from './UserPermissions';
|
||||
@ -119,7 +119,7 @@ export class UserAdminPage extends PureComponent<Props> {
|
||||
onUserEnable={this.onUserEnable}
|
||||
onPasswordChange={this.onPasswordChange}
|
||||
/>
|
||||
{isLDAPUser && config.licenseInfo.hasLicense && ldapSyncInfo && canReadLDAPStatus && (
|
||||
{isLDAPUser && featureEnabled('ldapsync') && ldapSyncInfo && canReadLDAPStatus && (
|
||||
<UserLdapSyncInfo ldapSyncInfo={ldapSyncInfo} user={user} onUserSync={this.onUserSync} />
|
||||
)}
|
||||
<UserPermissions isGrafanaAdmin={user.isGrafanaAdmin} onGrafanaAdminChange={this.onGrafanaAdminChange} />
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
import { NavModel } from '@grafana/data';
|
||||
import { featureEnabled } from '@grafana/runtime';
|
||||
import { Alert, Button, LegacyForms } from '@grafana/ui';
|
||||
const { FormField } = LegacyForms;
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import config from 'app/core/config';
|
||||
import Page from 'app/core/components/Page/Page';
|
||||
import { LdapConnectionStatus } from './LdapConnectionStatus';
|
||||
import { LdapSyncInfo } from './LdapSyncInfo';
|
||||
@ -99,7 +99,7 @@ export class LdapPage extends PureComponent<Props, State> {
|
||||
|
||||
<LdapConnectionStatus ldapConnectionInfo={ldapConnectionInfo} />
|
||||
|
||||
{config.licenseInfo.hasLicense && ldapSyncInfo && <LdapSyncInfo ldapSyncInfo={ldapSyncInfo} />}
|
||||
{featureEnabled('ldapsync') && ldapSyncInfo && <LdapSyncInfo ldapSyncInfo={ldapSyncInfo} />}
|
||||
|
||||
{canReadLDAPUser && (
|
||||
<>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import config from 'app/core/config';
|
||||
import { dateTimeFormat, dateTimeFormatTimeAgo } from '@grafana/data';
|
||||
import { getBackendSrv, locationService } from '@grafana/runtime';
|
||||
import { featureEnabled, getBackendSrv, locationService } from '@grafana/runtime';
|
||||
import { ThunkResult, LdapUser, UserSession, UserDTO, AccessControlAction, UserFilter } from 'app/types';
|
||||
|
||||
import {
|
||||
@ -35,7 +35,7 @@ export function loadAdminUserPage(userId: number): ThunkResult<void> {
|
||||
await dispatch(loadUserProfile(userId));
|
||||
await dispatch(loadUserOrgs(userId));
|
||||
await dispatch(loadUserSessions(userId));
|
||||
if (config.ldapEnabled && config.licenseInfo.hasLicense) {
|
||||
if (config.ldapEnabled && featureEnabled('ldapsync')) {
|
||||
await dispatch(loadLdapSyncStatus());
|
||||
}
|
||||
dispatch(userAdminPageLoadedAction(true));
|
||||
@ -183,7 +183,7 @@ export function loadLdapSyncStatus(): ThunkResult<void> {
|
||||
return async (dispatch) => {
|
||||
// Available only in enterprise
|
||||
const canReadLDAPStatus = contextSrv.hasPermission(AccessControlAction.LDAPStatusRead);
|
||||
if (config.licenseInfo.hasLicense && canReadLDAPStatus) {
|
||||
if (featureEnabled('ldapsync') && canReadLDAPStatus) {
|
||||
const syncStatus = await getBackendSrv().get(`/api/admin/ldap-sync-status`);
|
||||
dispatch(ldapSyncStatusLoadedAction(syncStatus));
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { DataSourcePluginMeta, PluginType } from '@grafana/data';
|
||||
import { config, featureEnabled } from '@grafana/runtime';
|
||||
import { DataSourcePluginCategory } from 'app/types';
|
||||
import { config } from '../../../core/config';
|
||||
|
||||
export function buildCategories(plugins: DataSourcePluginMeta[]): DataSourcePluginCategory[] {
|
||||
const categories: DataSourcePluginCategory[] = [
|
||||
@ -23,14 +23,12 @@ export function buildCategories(plugins: DataSourcePluginMeta[]): DataSourcePlug
|
||||
categoryIndex[category.id] = category;
|
||||
}
|
||||
|
||||
const { edition, hasValidLicense } = config.licenseInfo;
|
||||
|
||||
for (const plugin of plugins) {
|
||||
const enterprisePlugin = enterprisePlugins.find((item) => item.id === plugin.id);
|
||||
// Force category for enterprise plugins
|
||||
if (plugin.enterprise || enterprisePlugin) {
|
||||
plugin.category = 'enterprise';
|
||||
plugin.unlicensed = edition !== 'Open Source' && !hasValidLicense;
|
||||
plugin.unlicensed = !featureEnabled('enterprise.plugins');
|
||||
plugin.info.links = enterprisePlugin?.info?.links || plugin.info.links;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { DataSourceSettings, PluginType, PluginInclude, NavModel, NavModelItem } from '@grafana/data';
|
||||
import { featureEnabled } from '@grafana/runtime';
|
||||
import config from 'app/core/config';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
import { AccessControlAction } from 'app/types';
|
||||
@ -47,7 +48,7 @@ export function buildNavModel(dataSource: DataSourceSettings, plugin: GenericDat
|
||||
});
|
||||
}
|
||||
|
||||
if (config.licenseInfo.hasLicense) {
|
||||
if (featureEnabled('dspermissions')) {
|
||||
if (contextSrv.hasPermission(AccessControlAction.DataSourcesPermissionsRead)) {
|
||||
navModel.children!.push({
|
||||
active: false,
|
||||
@ -57,7 +58,9 @@ export function buildNavModel(dataSource: DataSourceSettings, plugin: GenericDat
|
||||
url: `datasources/edit/${dataSource.id}/permissions`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (featureEnabled('analytics')) {
|
||||
navModel.children!.push({
|
||||
active: false,
|
||||
icon: 'info-circle',
|
||||
@ -65,7 +68,9 @@ export function buildNavModel(dataSource: DataSourceSettings, plugin: GenericDat
|
||||
text: 'Insights',
|
||||
url: `datasources/edit/${dataSource.id}/insights`,
|
||||
});
|
||||
}
|
||||
|
||||
if (featureEnabled('caching')) {
|
||||
navModel.children!.push({
|
||||
active: false,
|
||||
icon: 'database',
|
||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { Badge, Button, HorizontalGroup, PluginSignatureBadge, useStyles2 } from '@grafana/ui';
|
||||
import { CatalogPlugin } from '../../types';
|
||||
import { getBadgeColor } from './sharedStyles';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { featureEnabled } from '@grafana/runtime';
|
||||
|
||||
type Props = { plugin: CatalogPlugin };
|
||||
|
||||
@ -17,7 +17,7 @@ export function PluginEnterpriseBadge({ plugin }: Props): React.ReactElement {
|
||||
);
|
||||
};
|
||||
|
||||
if (config.licenseInfo?.hasValidLicense) {
|
||||
if (featureEnabled('enterprise.plugins')) {
|
||||
return <Badge text="Enterprise" color="blue" />;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
import { config } from '@grafana/runtime';
|
||||
import { config, featureEnabled } from '@grafana/runtime';
|
||||
import { HorizontalGroup, Icon, LinkButton, useStyles2 } from '@grafana/ui';
|
||||
import { GrafanaTheme2, PluginType } from '@grafana/data';
|
||||
|
||||
@ -39,7 +39,7 @@ export const InstallControls = ({ plugin, latestCompatibleVersion }: Props) => {
|
||||
return <div className={styles.message}>Renderer plugins cannot be managed by the Plugin Catalog.</div>;
|
||||
}
|
||||
|
||||
if (plugin.isEnterprise && !config.licenseInfo?.hasValidLicense) {
|
||||
if (plugin.isEnterprise && !featureEnabled('enterprise.plugins')) {
|
||||
return (
|
||||
<HorizontalGroup height="auto" align="center">
|
||||
<span className={styles.message}>No valid Grafana Enterprise license detected.</span>
|
||||
|
@ -49,14 +49,14 @@ describe('PluginListItemBadges', () => {
|
||||
});
|
||||
|
||||
it('renders an enterprise badge (when a license is valid)', () => {
|
||||
config.licenseInfo.hasValidLicense = true;
|
||||
config.licenseInfo.enabledFeatures = { 'enterprise.plugins': true };
|
||||
render(<PluginListItemBadges plugin={{ ...plugin, isEnterprise: true }} />);
|
||||
expect(screen.getByText(/enterprise/i)).toBeVisible();
|
||||
expect(screen.queryByRole('button', { name: /learn more/i })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders an enterprise badge with icon and link (when a license is invalid)', () => {
|
||||
config.licenseInfo.hasValidLicense = false;
|
||||
config.licenseInfo.enabledFeatures = {};
|
||||
render(<PluginListItemBadges plugin={{ ...plugin, isEnterprise: true }} />);
|
||||
expect(screen.getByText(/enterprise/i)).toBeVisible();
|
||||
expect(screen.getByLabelText(/lock icon/i)).toBeInTheDocument();
|
||||
|
@ -90,7 +90,7 @@ describe('Plugin details page', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
config.pluginAdminExternalManageEnabled = false;
|
||||
config.licenseInfo.hasValidLicense = false;
|
||||
config.licenseInfo.enabledFeatures = {};
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
@ -325,7 +325,7 @@ describe('Plugin details page', () => {
|
||||
});
|
||||
|
||||
it('should display an install button for enterprise plugins if license is valid', async () => {
|
||||
config.licenseInfo.hasValidLicense = true;
|
||||
config.licenseInfo.enabledFeatures = { 'enterprise.plugins': true };
|
||||
|
||||
const { queryByRole } = renderPluginDetails({ id, isInstalled: false, isEnterprise: true });
|
||||
|
||||
@ -333,7 +333,7 @@ describe('Plugin details page', () => {
|
||||
});
|
||||
|
||||
it('should not display install button for enterprise plugins if license is invalid', async () => {
|
||||
config.licenseInfo.hasValidLicense = false;
|
||||
config.licenseInfo.enabledFeatures = {};
|
||||
|
||||
const { queryByRole, queryByText } = renderPluginDetails({ id, isInstalled: true, isEnterprise: true });
|
||||
|
||||
@ -772,7 +772,7 @@ describe('Plugin details page', () => {
|
||||
});
|
||||
|
||||
it('should not display an install button for enterprise plugins if license is valid', async () => {
|
||||
config.licenseInfo.hasValidLicense = true;
|
||||
config.licenseInfo.enabledFeatures = { 'enterprise.plugins': true };
|
||||
const { queryByRole, queryByText } = renderPluginDetails({ id, isInstalled: false, isEnterprise: true });
|
||||
|
||||
await waitFor(() => expect(queryByText(PluginTabLabels.OVERVIEW)).toBeInTheDocument());
|
||||
|
@ -7,10 +7,12 @@ import { User } from 'app/core/services/context_srv';
|
||||
import { NavModel } from '@grafana/data';
|
||||
import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps';
|
||||
|
||||
jest.mock('app/core/config', () => ({
|
||||
...((jest.requireActual('app/core/config') as unknown) as object),
|
||||
licenseInfo: {
|
||||
hasLicense: true,
|
||||
jest.mock('@grafana/runtime/src/config', () => ({
|
||||
...((jest.requireActual('@grafana/runtime/src/config') as unknown) as object),
|
||||
config: {
|
||||
licenseInfo: {
|
||||
enabledFeatures: { teamsync: true },
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
|
@ -13,6 +13,7 @@ import { getTeamLoadingNav } from './state/navModel';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import { NavModel } from '@grafana/data';
|
||||
import { featureEnabled } from '@grafana/runtime';
|
||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
||||
|
||||
interface TeamPageRouteParams {
|
||||
@ -67,7 +68,7 @@ export class TeamPages extends PureComponent<Props, State> {
|
||||
|
||||
this.state = {
|
||||
isLoading: false,
|
||||
isSyncEnabled: config.licenseInfo.hasLicense,
|
||||
isSyncEnabled: featureEnabled('teamsync'),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Team, TeamPermissionLevel } from 'app/types';
|
||||
import config from 'app/core/config';
|
||||
import { featureEnabled } from '@grafana/runtime';
|
||||
import { NavModelItem, NavModel } from '@grafana/data';
|
||||
|
||||
export function buildNavModel(team: Team): NavModelItem {
|
||||
@ -28,7 +28,7 @@ export function buildNavModel(team: Team): NavModelItem {
|
||||
],
|
||||
};
|
||||
|
||||
if (config.licenseInfo.hasLicense) {
|
||||
if (featureEnabled('teamsync')) {
|
||||
navModel.children.push({
|
||||
active: false,
|
||||
icon: 'sync',
|
||||
|
Loading…
Reference in New Issue
Block a user