Chore: Add type info for grafana boot data (#45322)

Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
This commit is contained in:
kay delaney 2022-03-30 10:48:58 +01:00 committed by GitHub
parent 6889e39526
commit f1c3177e79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 142 additions and 65 deletions

View File

@ -200,7 +200,7 @@ exports[`no enzyme tests`] = {
"public/app/features/dashboard/components/SaveDashboard/forms/SaveDashboardForm.test.tsx:1262111696": [ "public/app/features/dashboard/components/SaveDashboard/forms/SaveDashboardForm.test.tsx:1262111696": [
[1, 17, 13, "RegExp match", "2409514259"] [1, 17, 13, "RegExp match", "2409514259"]
], ],
"public/app/features/dashboard/components/ShareModal/ShareLink.test.tsx:1044891955": [ "public/app/features/dashboard/components/ShareModal/ShareLink.test.tsx:809006195": [
[1, 35, 13, "RegExp match", "2409514259"] [1, 35, 13, "RegExp match", "2409514259"]
], ],
"public/app/features/dashboard/dashgrid/DashboardGrid.test.tsx:1798654441": [ "public/app/features/dashboard/dashgrid/DashboardGrid.test.tsx:1798654441": [

View File

@ -5,6 +5,7 @@ import { SystemDateFormatSettings } from '../datetime';
import { GrafanaTheme2 } from '../themes'; import { GrafanaTheme2 } from '../themes';
import { MapLayerOptions } from '../geo/layer'; import { MapLayerOptions } from '../geo/layer';
import { FeatureToggles } from './featureToggles.gen'; import { FeatureToggles } from './featureToggles.gen';
import { NavLinkDTO, OrgRole } from '.';
/** /**
* Describes the build information that will be available via the Grafana configuration. * Describes the build information that will be available via the Grafana configuration.
@ -85,10 +86,47 @@ export type OAuth =
*/ */
export type OAuthSettings = Partial<Record<OAuth, { name: string; icon?: string }>>; export type OAuthSettings = Partial<Record<OAuth, { name: string; icon?: string }>>;
/** Current user info included in bootData
*
* @internal
*/
export interface CurrentUserDTO {
isSignedIn: boolean;
id: number;
login: string;
email: string;
name: string;
lightTheme: boolean;
orgCount: number;
orgId: number;
orgName: string;
orgRole: OrgRole | '';
isGrafanaAdmin: boolean;
gravatarUrl: string;
timezone: string;
weekStart: string;
locale: string;
permissions?: Record<string, boolean>;
}
/** Contains essential user and config info
*
* @internal
*/
export interface BootData {
user: CurrentUserDTO;
settings: GrafanaConfig;
navTree: NavLinkDTO[];
themePaths: {
light: string;
dark: string;
};
}
/** /**
* Describes all the different Grafana configuration values available for an instance. * Describes all the different Grafana configuration values available for an instance.
* *
* @public * @internal
*/ */
export interface GrafanaConfig { export interface GrafanaConfig {
datasources: { [str: string]: DataSourceInstanceSettings }; datasources: { [str: string]: DataSourceInstanceSettings };
@ -98,7 +136,7 @@ export interface GrafanaConfig {
windowTitlePrefix: string; windowTitlePrefix: string;
buildInfo: BuildInfo; buildInfo: BuildInfo;
newPanelTitle: string; newPanelTitle: string;
bootData: any; bootData: BootData;
externalUserMngLinkUrl: string; externalUserMngLinkUrl: string;
externalUserMngLinkName: string; externalUserMngLinkName: string;
externalUserMngInfo: string; externalUserMngInfo: string;
@ -120,9 +158,9 @@ export interface GrafanaConfig {
verifyEmailEnabled: boolean; verifyEmailEnabled: boolean;
oauth: OAuthSettings; oauth: OAuthSettings;
disableUserSignUp: boolean; disableUserSignUp: boolean;
loginHint: any; loginHint: string;
passwordHint: any; passwordHint: string;
loginError: any; loginError?: string;
navTree: any; navTree: any;
viewersCanEdit: boolean; viewersCanEdit: boolean;
editorsCanAdmin: boolean; editorsCanAdmin: boolean;

View File

@ -36,7 +36,16 @@ export * from './live';
export * from './variables'; export * from './variables';
export * from './geometry'; export * from './geometry';
export { isUnsignedPluginSignature } from './pluginSignature'; export { isUnsignedPluginSignature } from './pluginSignature';
export { OAuth, OAuthSettings, GrafanaConfig, BuildInfo, LicenseInfo, PreloadPlugin } from './config'; export {
CurrentUserDTO,
BootData,
OAuth,
OAuthSettings,
GrafanaConfig,
BuildInfo,
LicenseInfo,
PreloadPlugin,
} from './config';
export { FeatureToggles } from './featureToggles.gen'; export { FeatureToggles } from './featureToggles.gen';
export * from './alerts'; export * from './alerts';
export * from './slider'; export * from './slider';

View File

@ -1,21 +1,28 @@
import { ComponentType } from 'react'; import { ComponentType } from 'react';
export interface NavModelItem { export interface NavLinkDTO {
id?: string;
text: string; text: string;
url?: string; description?: string;
section?: NavSection;
subTitle?: string; subTitle?: string;
icon?: string; icon?: string;
img?: string; img?: string;
id?: string; url?: string;
active?: boolean;
hideFromTabs?: boolean;
hideFromMenu?: boolean;
divider?: boolean;
children?: NavModelItem[];
breadcrumbs?: NavModelBreadcrumb[];
target?: string; target?: string;
sortWeight?: number;
divider?: boolean;
hideFromMenu?: boolean;
hideFromTabs?: boolean;
children?: NavLinkDTO[];
highlightText?: string;
}
export interface NavModelItem extends NavLinkDTO {
children?: NavModelItem[];
active?: boolean;
breadcrumbs?: NavModelBreadcrumb[];
parentItem?: NavModelItem; parentItem?: NavModelItem;
section?: NavSection;
showOrgSwitcher?: boolean; showOrgSwitcher?: boolean;
onClick?: () => void; onClick?: () => void;
menuItemType?: NavMenuItemType; menuItemType?: NavMenuItemType;

View File

@ -1,5 +1,6 @@
import { merge } from 'lodash'; import { merge } from 'lodash';
import { import {
BootData,
BuildInfo, BuildInfo,
createTheme, createTheme,
DataSourceInstanceSettings, DataSourceInstanceSettings,
@ -28,9 +29,9 @@ export class GrafanaBootConfig implements GrafanaConfig {
appUrl = ''; appUrl = '';
appSubUrl = ''; appSubUrl = '';
windowTitlePrefix = ''; windowTitlePrefix = '';
buildInfo: BuildInfo = {} as BuildInfo; buildInfo: BuildInfo;
newPanelTitle = ''; newPanelTitle = '';
bootData: any; bootData: BootData;
externalUserMngLinkUrl = ''; externalUserMngLinkUrl = '';
externalUserMngLinkName = ''; externalUserMngLinkName = '';
externalUserMngInfo = ''; externalUserMngInfo = '';
@ -54,9 +55,9 @@ export class GrafanaBootConfig implements GrafanaConfig {
verifyEmailEnabled = false; verifyEmailEnabled = false;
oauth: OAuthSettings = {}; oauth: OAuthSettings = {};
disableUserSignUp = false; disableUserSignUp = false;
loginHint: any; loginHint = '';
passwordHint: any; passwordHint = '';
loginError: any; loginError = undefined;
navTree: any; navTree: any;
viewersCanEdit = false; viewersCanEdit = false;
editorsCanAdmin = false; editorsCanAdmin = false;
@ -117,6 +118,8 @@ export class GrafanaBootConfig implements GrafanaConfig {
const mode = options.bootData.user.lightTheme ? 'light' : 'dark'; const mode = options.bootData.user.lightTheme ? 'light' : 'dark';
this.theme2 = createTheme({ colors: { mode } }); this.theme2 = createTheme({ colors: { mode } });
this.theme = this.theme2.v1; this.theme = this.theme2.v1;
this.bootData = options.bootData;
this.buildInfo = options.buildInfo;
const defaults = { const defaults = {
datasources: {}, datasources: {},

View File

@ -57,7 +57,7 @@ const (
type NavLink struct { type NavLink struct {
Id string `json:"id,omitempty"` Id string `json:"id,omitempty"`
Text string `json:"text,omitempty"` Text string `json:"text"`
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
Section string `json:"section,omitempty"` Section string `json:"section,omitempty"`
SubTitle string `json:"subTitle,omitempty"` SubTitle string `json:"subTitle,omitempty"`

View File

@ -1,46 +1,49 @@
import coreModule from 'app/angular/core_module'; import coreModule from 'app/angular/core_module';
import config from 'app/core/config'; import config from 'app/core/config';
import { find, isNumber } from 'lodash'; import { NavModel, NavModelItem } from '@grafana/data';
import { NavModel } from '@grafana/data';
interface Nav {
breadcrumbs: NavModelItem[];
node?: NavModelItem;
main?: NavModelItem;
}
export class NavModelSrv { export class NavModelSrv {
navItems: any; navItems: NavModelItem[];
constructor() { constructor() {
this.navItems = config.bootData.navTree; this.navItems = config.bootData.navTree;
} }
getCfgNode() { getCfgNode() {
return find(this.navItems, { id: 'cfg' }); return this.navItems.find((navItem) => navItem.id === 'cfg');
} }
getNav(...args: Array<string | number>) { getNav(...args: Array<string | number>) {
let children = this.navItems; let children = this.navItems;
const nav = { const nav: Nav = {
breadcrumbs: [], breadcrumbs: [],
} as any; };
for (const id of args) { for (const id of args) {
// if its a number then it's the index to use for main // if its a number then it's the index to use for main
if (isNumber(id)) { if (typeof id === 'number') {
nav.main = nav.breadcrumbs[id]; nav.main = nav.breadcrumbs[id];
break; break;
} }
const node: any = find(children, { id: id }); const node = children.find((child) => child.id === id);
nav.breadcrumbs.push(node); if (node) {
nav.node = node; nav.breadcrumbs.push(node);
nav.main = node; nav.node = node;
children = node.children; nav.main = node;
children = node.children ?? [];
}
} }
if (nav.main.children) { if (nav.main?.children) {
for (const item of nav.main.children) { for (const item of nav.main.children) {
item.active = false; item.active = item.url === nav.node?.url;
if (item.url === nav.node.url) {
item.active = true;
}
} }
} }

View File

@ -1,25 +1,30 @@
import config from '../../core/config'; import config from '../../core/config';
import { extend } from 'lodash'; import { extend } from 'lodash';
import { rangeUtil, WithAccessControlMetadata } from '@grafana/data'; import { OrgRole, rangeUtil, WithAccessControlMetadata } from '@grafana/data';
import { AccessControlAction, UserPermission } from 'app/types'; import { AccessControlAction, UserPermission } from 'app/types';
import { featureEnabled, getBackendSrv } from '@grafana/runtime'; import { featureEnabled, getBackendSrv } from '@grafana/runtime';
import { CurrentUserInternal } from 'app/types/config';
export class User { export class User implements CurrentUserInternal {
isSignedIn: boolean;
id: number; id: number;
isGrafanaAdmin: any; login: string;
isSignedIn: any; email: string;
orgRole: any; name: string;
lightTheme: boolean;
orgCount: number;
orgId: number; orgId: number;
orgName: string; orgName: string;
login: string; orgRole: OrgRole | '';
orgCount: number; isGrafanaAdmin: boolean;
gravatarUrl: string;
timezone: string; timezone: string;
fiscalYearStartMonth: number; weekStart: string;
locale: string;
helpFlags1: number; helpFlags1: number;
lightTheme: boolean;
hasEditPermissionInFolders: boolean; hasEditPermissionInFolders: boolean;
email?: string;
permissions?: UserPermission; permissions?: UserPermission;
fiscalYearStartMonth: number;
constructor() { constructor() {
this.id = 0; this.id = 0;
@ -35,7 +40,12 @@ export class User {
this.helpFlags1 = 0; this.helpFlags1 = 0;
this.lightTheme = false; this.lightTheme = false;
this.hasEditPermissionInFolders = false; this.hasEditPermissionInFolders = false;
this.email = undefined; this.email = '';
this.name = '';
this.locale = '';
this.weekStart = '';
this.gravatarUrl = '';
if (config.bootData.user) { if (config.bootData.user) {
extend(this, config.bootData.user); extend(this, config.bootData.user);
} }
@ -55,7 +65,7 @@ export class ContextSrv {
constructor() { constructor() {
if (!config.bootData) { if (!config.bootData) {
config.bootData = { user: {}, settings: {} }; config.bootData = { user: {}, settings: {} } as any;
} }
this.user = new User(); this.user = new User();
@ -180,7 +190,7 @@ export { contextSrv };
export const setContextSrv = (override: ContextSrv) => { export const setContextSrv = (override: ContextSrv) => {
if (process.env.NODE_ENV !== 'test') { if (process.env.NODE_ENV !== 'test') {
throw new Error('contextSrv can be only overriden in test environment'); throw new Error('contextSrv can be only overridden in test environment');
} }
contextSrv = override; contextSrv = override;
}; };

View File

@ -53,7 +53,7 @@ describe('ShareEmbed', () => {
user: { user: {
orgId: 1, orgId: 1,
}, },
}; } as any;
}); });
afterAll(() => { afterAll(() => {

View File

@ -110,7 +110,7 @@ describe('ShareModal', () => {
user: { user: {
orgId: 1, orgId: 1,
}, },
}; } as any;
ctx.mount({ ctx.mount({
panel: new PanelModel({ id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } }), panel: new PanelModel({ id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } }),
}); });
@ -202,7 +202,7 @@ describe('when default_home_dashboard_path is set in the grafana config', () =>
user: { user: {
orgId: 1, orgId: 1,
}, },
}; } as any;
}); });
afterAll(() => { afterAll(() => {

View File

@ -36,7 +36,7 @@ describe('buildParams', () => {
to: 2000, to: 2000,
raw: { from: 'now-6h', to: 'now' }, raw: { from: 'now-6h', to: 'now' },
} as unknown as TimeRange; } as unknown as TimeRange;
const orgId = '2'; const orgId = 2;
const result = buildParams({ useCurrentTimeRange, selectedTheme, panel, search, range, orgId }); const result = buildParams({ useCurrentTimeRange, selectedTheme, panel, search, range, orgId });
expect(result.toString()).toEqual(expected); expect(result.toString()).toEqual(expected);

View File

@ -9,7 +9,7 @@ export interface BuildParamsArgs {
panel?: PanelModel; panel?: PanelModel;
search?: string; search?: string;
range?: TimeRange; range?: TimeRange;
orgId?: string; orgId?: number;
} }
export function buildParams({ export function buildParams({
@ -24,7 +24,7 @@ export function buildParams({
searchParams.set('from', String(range.from.valueOf())); searchParams.set('from', String(range.from.valueOf()));
searchParams.set('to', String(range.to.valueOf())); searchParams.set('to', String(range.to.valueOf()));
searchParams.set('orgId', orgId); searchParams.set('orgId', String(orgId));
if (!useCurrentTimeRange) { if (!useCurrentTimeRange) {
searchParams.delete('from'); searchParams.delete('from');

View File

@ -44,7 +44,7 @@ describe('grafanaGraph', () => {
user: { user: {
lightTheme: false, lightTheme: false,
}, },
}; } as any;
Object.assign(GraphCtrl.prototype, { Object.assign(GraphCtrl.prototype, {
...MetricsPanelCtrl.prototype, ...MetricsPanelCtrl.prototype,
...PanelCtrl.prototype, ...PanelCtrl.prototype,

View File

@ -1,12 +1,10 @@
/** /**
* UserPermission is a map storing permissions in a form of * UserPermission is a map storing permissions in a form of
* { * {
* action: { scope: scope } * action: true;
* } * }
*/ */
export type UserPermission = { export type UserPermission = Record<string, boolean>;
[key: string]: { [key: string]: string };
};
// Permission actions // Permission actions
export enum AccessControlAction { export enum AccessControlAction {

View File

@ -0,0 +1,9 @@
import { CurrentUserDTO } from '@grafana/data';
/**
* Extends `CurrentUserDTO` with some properties meant only for internal use.
*/
export interface CurrentUserInternal extends CurrentUserDTO {
helpFlags1: number;
hasEditPermissionInFolders: boolean;
}