mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat: dashboard permissions are working
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
AclTarget,
|
||||
PermissionLevel,
|
||||
NewDashboardAclItem,
|
||||
OrgRole,
|
||||
} from 'app/types/acl';
|
||||
|
||||
export interface Props {
|
||||
@@ -25,7 +26,7 @@ class AddPermissions extends Component<Props, NewDashboardAclItem> {
|
||||
return {
|
||||
userId: 0,
|
||||
teamId: 0,
|
||||
role: '',
|
||||
role: OrgRole.Viewer,
|
||||
type: AclTarget.Team,
|
||||
permission: PermissionLevel.View,
|
||||
};
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import AddPermissions from './AddPermissions';
|
||||
import { RootStore } from 'app/stores/RootStore/RootStore';
|
||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||
|
||||
jest.mock('app/core/services/backend_srv', () => ({
|
||||
getBackendSrv: () => {
|
||||
return {
|
||||
get: () => {
|
||||
return Promise.resolve([
|
||||
{ id: 2, dashboardId: 1, role: 'Viewer', permission: 1, permissionName: 'View' },
|
||||
{ id: 3, dashboardId: 1, role: 'Editor', permission: 1, permissionName: 'Edit' },
|
||||
]);
|
||||
},
|
||||
post: jest.fn(() => Promise.resolve({})),
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
describe('AddPermissions', () => {
|
||||
let wrapper;
|
||||
let store;
|
||||
let instance;
|
||||
const backendSrv: any = getBackendSrv();
|
||||
|
||||
beforeAll(() => {
|
||||
store = RootStore.create({}, { backendSrv: backendSrv });
|
||||
wrapper = shallow(<AddPermissions permissions={store.permissions} />);
|
||||
instance = wrapper.instance();
|
||||
return store.permissions.load(1, true, false);
|
||||
});
|
||||
|
||||
describe('when permission for a user is added', () => {
|
||||
it('should save permission to db', () => {
|
||||
const evt = {
|
||||
target: {
|
||||
value: 'User',
|
||||
},
|
||||
};
|
||||
const userItem = {
|
||||
id: 2,
|
||||
login: 'user2',
|
||||
};
|
||||
|
||||
instance.onTypeChanged(evt);
|
||||
instance.onUserSelected(userItem);
|
||||
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('[data-save-permission]').prop('disabled')).toBe(false);
|
||||
|
||||
wrapper.find('form').simulate('submit', { preventDefault() {} });
|
||||
|
||||
expect(backendSrv.post.mock.calls.length).toBe(1);
|
||||
expect(backendSrv.post.mock.calls[0][0]).toBe('/api/dashboards/id/1/permissions');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when permission for team is added', () => {
|
||||
it('should save permission to db', () => {
|
||||
const evt = {
|
||||
target: {
|
||||
value: 'Group',
|
||||
},
|
||||
};
|
||||
|
||||
const teamItem = {
|
||||
id: 2,
|
||||
name: 'ug1',
|
||||
};
|
||||
|
||||
instance.onTypeChanged(evt);
|
||||
instance.onTeamSelected(teamItem);
|
||||
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('[data-save-permission]').prop('disabled')).toBe(false);
|
||||
|
||||
wrapper.find('form').simulate('submit', { preventDefault() {} });
|
||||
|
||||
expect(backendSrv.post.mock.calls.length).toBe(1);
|
||||
expect(backendSrv.post.mock.calls[0][0]).toBe('/api/dashboards/id/1/permissions');
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
backendSrv.post.mockClear();
|
||||
});
|
||||
});
|
||||
@@ -1,128 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { aclTypes } from 'app/stores/PermissionsStore/PermissionsStore';
|
||||
import { UserPicker, User } from 'app/core/components/Picker/UserPicker';
|
||||
import { TeamPicker, Team } from 'app/core/components/Picker/TeamPicker';
|
||||
import DescriptionPicker, { OptionWithDescription } from 'app/core/components/Picker/DescriptionPicker';
|
||||
import { permissionOptions } from 'app/stores/PermissionsStore/PermissionsStore';
|
||||
|
||||
export interface Props {
|
||||
permissions: any;
|
||||
}
|
||||
|
||||
@observer
|
||||
class AddPermissions extends Component<Props, any> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const { permissions } = this.props;
|
||||
permissions.resetNewType();
|
||||
}
|
||||
|
||||
onTypeChanged = evt => {
|
||||
const { value } = evt.target;
|
||||
const { permissions } = this.props;
|
||||
|
||||
permissions.setNewType(value);
|
||||
};
|
||||
|
||||
onUserSelected = (user: User) => {
|
||||
const { permissions } = this.props;
|
||||
if (!user) {
|
||||
permissions.newItem.setUser(null, null);
|
||||
return;
|
||||
}
|
||||
return permissions.newItem.setUser(user.id, user.login, user.avatarUrl);
|
||||
};
|
||||
|
||||
onTeamSelected = (team: Team) => {
|
||||
const { permissions } = this.props;
|
||||
if (!team) {
|
||||
permissions.newItem.setTeam(null, null);
|
||||
return;
|
||||
}
|
||||
return permissions.newItem.setTeam(team.id, team.name, team.avatarUrl);
|
||||
};
|
||||
|
||||
onPermissionChanged = (permission: OptionWithDescription) => {
|
||||
const { permissions } = this.props;
|
||||
return permissions.newItem.setPermission(permission.value);
|
||||
};
|
||||
|
||||
resetNewType() {
|
||||
const { permissions } = this.props;
|
||||
return permissions.resetNewType();
|
||||
}
|
||||
|
||||
onSubmit = evt => {
|
||||
evt.preventDefault();
|
||||
const { permissions } = this.props;
|
||||
permissions.addStoreItem();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { permissions } = this.props;
|
||||
const newItem = permissions.newItem;
|
||||
const pickerClassName = 'width-20';
|
||||
|
||||
const isValid = newItem.isValid();
|
||||
|
||||
return (
|
||||
<div className="gf-form-inline cta-form">
|
||||
<button className="cta-form__close btn btn-transparent" onClick={permissions.hideAddPermissions}>
|
||||
<i className="fa fa-close" />
|
||||
</button>
|
||||
<form name="addPermission" onSubmit={this.onSubmit}>
|
||||
<h5>Add Permission For</h5>
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<div className="gf-form-select-wrapper">
|
||||
<select className="gf-form-input gf-size-auto" value={newItem.type} onChange={this.onTypeChanged}>
|
||||
{aclTypes.map((option, idx) => {
|
||||
return (
|
||||
<option key={idx} value={option.value}>
|
||||
{option.text}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{newItem.type === 'User' ? (
|
||||
<div className="gf-form">
|
||||
<UserPicker onSelected={this.onUserSelected} value={newItem.userId} className={pickerClassName} />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{newItem.type === 'Group' ? (
|
||||
<div className="gf-form">
|
||||
<TeamPicker onSelected={this.onTeamSelected} value={newItem.teamId} className={pickerClassName} />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="gf-form">
|
||||
<DescriptionPicker
|
||||
optionsWithDesc={permissionOptions}
|
||||
onSelected={this.onPermissionChanged}
|
||||
value={newItem.permission}
|
||||
disabled={false}
|
||||
className={'gf-form-input--form-dropdown-right'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="gf-form">
|
||||
<button data-save-permission className="btn btn-success" type="submit" disabled={!isValid}>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AddPermissions;
|
||||
@@ -1,71 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { store } from 'app/stores/store';
|
||||
import Permissions from 'app/core/components/Permissions/Permissions';
|
||||
import Tooltip from 'app/core/components/Tooltip/Tooltip';
|
||||
import PermissionsInfo from 'app/core/components/Permissions/PermissionsInfo';
|
||||
import AddPermissions from 'app/core/components/Permissions/AddPermissions';
|
||||
import SlideDown from 'app/core/components/Animations/SlideDown';
|
||||
import { FolderInfo } from './FolderInfo';
|
||||
|
||||
export interface Props {
|
||||
dashboardId: number;
|
||||
folder?: FolderInfo;
|
||||
backendSrv: any;
|
||||
}
|
||||
|
||||
@observer
|
||||
class DashboardPermissions extends Component<Props, any> {
|
||||
permissions: any;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleAddPermission = this.handleAddPermission.bind(this);
|
||||
this.permissions = store.permissions;
|
||||
}
|
||||
|
||||
handleAddPermission() {
|
||||
this.permissions.toggleAddPermissions();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.permissions.hideAddPermissions();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { dashboardId, folder, backendSrv } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="dashboard-settings__header">
|
||||
<div className="page-action-bar">
|
||||
<h3 className="d-inline-block">Permissions</h3>
|
||||
<Tooltip className="page-sub-heading-icon" placement="auto" content={PermissionsInfo}>
|
||||
<i className="gicon gicon-question gicon--has-hover" />
|
||||
</Tooltip>
|
||||
<div className="page-action-bar__spacer" />
|
||||
<button
|
||||
className="btn btn-success pull-right"
|
||||
onClick={this.handleAddPermission}
|
||||
disabled={this.permissions.isAddPermissionsVisible}
|
||||
>
|
||||
<i className="fa fa-plus" /> Add Permission
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<SlideDown in={this.permissions.isAddPermissionsVisible}>
|
||||
<AddPermissions permissions={this.permissions} />
|
||||
</SlideDown>
|
||||
<Permissions
|
||||
permissions={this.permissions}
|
||||
isFolder={false}
|
||||
dashboardId={dashboardId}
|
||||
folderInfo={folder}
|
||||
backendSrv={backendSrv}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DashboardPermissions;
|
||||
@@ -1,43 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import DescriptionPicker from 'app/core/components/Picker/DescriptionPicker';
|
||||
import { permissionOptions } from 'app/stores/PermissionsStore/PermissionsStore';
|
||||
|
||||
export interface Props {
|
||||
item: any;
|
||||
}
|
||||
|
||||
export default class DisabledPermissionListItem extends Component<Props, any> {
|
||||
render() {
|
||||
const { item } = this.props;
|
||||
|
||||
return (
|
||||
<tr className="gf-form-disabled">
|
||||
<td style={{ width: '1%' }}>
|
||||
<i style={{ width: '25px', height: '25px' }} className="gicon gicon-shield" />
|
||||
</td>
|
||||
<td style={{ width: '90%' }}>
|
||||
{item.name}
|
||||
<span className="filter-table__weak-italic"> (Role)</span>
|
||||
</td>
|
||||
<td />
|
||||
<td className="query-keyword">Can</td>
|
||||
<td>
|
||||
<div className="gf-form">
|
||||
<DescriptionPicker
|
||||
optionsWithDesc={permissionOptions}
|
||||
onSelected={() => {}}
|
||||
value={item.permission}
|
||||
disabled={true}
|
||||
className={'gf-form-input--form-dropdown-right'}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button className="btn btn-inverse btn-small">
|
||||
<i className="fa fa-lock" />
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export interface FolderInfo {
|
||||
id: number;
|
||||
title: string;
|
||||
url: string;
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import PermissionsList from './PermissionsList';
|
||||
import { observer } from 'mobx-react';
|
||||
import { FolderInfo } from './FolderInfo';
|
||||
|
||||
export interface DashboardAcl {
|
||||
id?: number;
|
||||
dashboardId?: number;
|
||||
userId?: number;
|
||||
userLogin?: string;
|
||||
userEmail?: string;
|
||||
teamId?: number;
|
||||
team?: string;
|
||||
permission?: number;
|
||||
permissionName?: string;
|
||||
role?: string;
|
||||
icon?: string;
|
||||
name?: string;
|
||||
inherited?: boolean;
|
||||
sortRank?: number;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
dashboardId: number;
|
||||
folderInfo?: FolderInfo;
|
||||
permissions?: any;
|
||||
isFolder: boolean;
|
||||
backendSrv: any;
|
||||
}
|
||||
|
||||
@observer
|
||||
class Permissions extends Component<Props, any> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { dashboardId, isFolder, folderInfo } = this.props;
|
||||
this.permissionChanged = this.permissionChanged.bind(this);
|
||||
this.typeChanged = this.typeChanged.bind(this);
|
||||
this.removeItem = this.removeItem.bind(this);
|
||||
this.loadStore(dashboardId, isFolder, folderInfo && folderInfo.id === 0);
|
||||
}
|
||||
|
||||
loadStore(dashboardId, isFolder, isInRoot = false) {
|
||||
return this.props.permissions.load(dashboardId, isFolder, isInRoot);
|
||||
}
|
||||
|
||||
permissionChanged(index: number, permission: number, permissionName: string) {
|
||||
const { permissions } = this.props;
|
||||
permissions.updatePermissionOnIndex(index, permission, permissionName);
|
||||
}
|
||||
|
||||
removeItem(index: number) {
|
||||
const { permissions } = this.props;
|
||||
permissions.removeStoreItem(index);
|
||||
}
|
||||
|
||||
resetNewType() {
|
||||
const { permissions } = this.props;
|
||||
permissions.resetNewType();
|
||||
}
|
||||
|
||||
typeChanged(evt) {
|
||||
const { value } = evt.target;
|
||||
const { permissions, dashboardId } = this.props;
|
||||
|
||||
if (value === 'Viewer' || value === 'Editor') {
|
||||
permissions.addStoreItem({ permission: 1, role: value, dashboardId: dashboardId }, dashboardId);
|
||||
this.resetNewType();
|
||||
return;
|
||||
}
|
||||
|
||||
permissions.setNewType(value);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { permissions, folderInfo } = this.props;
|
||||
|
||||
return (
|
||||
<div className="gf-form-group">
|
||||
<PermissionsList
|
||||
permissions={permissions.items}
|
||||
removeItem={this.removeItem}
|
||||
permissionChanged={this.permissionChanged}
|
||||
fetching={permissions.fetching}
|
||||
folderInfo={folderInfo}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Permissions;
|
||||
@@ -1,64 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import PermissionsListItem from './PermissionsListItem';
|
||||
import DisabledPermissionsListItem from './DisabledPermissionsListItem';
|
||||
import { observer } from 'mobx-react';
|
||||
import { FolderInfo } from './FolderInfo';
|
||||
|
||||
export interface Props {
|
||||
permissions: any[];
|
||||
removeItem: any;
|
||||
permissionChanged: any;
|
||||
fetching: boolean;
|
||||
folderInfo?: FolderInfo;
|
||||
}
|
||||
|
||||
@observer
|
||||
class PermissionsList extends Component<Props, any> {
|
||||
render() {
|
||||
const { permissions, removeItem, permissionChanged, fetching, folderInfo } = this.props;
|
||||
|
||||
return (
|
||||
<table className="filter-table gf-form-group">
|
||||
<tbody>
|
||||
<DisabledPermissionsListItem
|
||||
key={0}
|
||||
item={{
|
||||
name: 'Admin',
|
||||
permission: 4,
|
||||
icon: 'fa fa-fw fa-street-view',
|
||||
}}
|
||||
/>
|
||||
{permissions.map((item, idx) => {
|
||||
return (
|
||||
<PermissionsListItem
|
||||
key={idx + 1}
|
||||
item={item}
|
||||
itemIndex={idx}
|
||||
removeItem={removeItem}
|
||||
permissionChanged={permissionChanged}
|
||||
folderInfo={folderInfo}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{fetching === true && permissions.length < 1 ? (
|
||||
<tr>
|
||||
<td colSpan={4}>
|
||||
<em>Loading permissions...</em>
|
||||
</td>
|
||||
</tr>
|
||||
) : null}
|
||||
|
||||
{fetching === false && permissions.length < 1 ? (
|
||||
<tr>
|
||||
<td colSpan={4}>
|
||||
<em>No permissions are set. Will only be accessible by admins.</em>
|
||||
</td>
|
||||
</tr>
|
||||
) : null}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PermissionsList;
|
||||
@@ -1,91 +0,0 @@
|
||||
import React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import DescriptionPicker from 'app/core/components/Picker/DescriptionPicker';
|
||||
import { permissionOptions } from 'app/stores/PermissionsStore/PermissionsStore';
|
||||
|
||||
const setClassNameHelper = inherited => {
|
||||
return inherited ? 'gf-form-disabled' : '';
|
||||
};
|
||||
|
||||
function ItemAvatar({ item }) {
|
||||
if (item.userAvatarUrl) {
|
||||
return <img className="filter-table__avatar" src={item.userAvatarUrl} />;
|
||||
}
|
||||
if (item.teamAvatarUrl) {
|
||||
return <img className="filter-table__avatar" src={item.teamAvatarUrl} />;
|
||||
}
|
||||
if (item.role === 'Editor') {
|
||||
return <i style={{ width: '25px', height: '25px' }} className="gicon gicon-editor" />;
|
||||
}
|
||||
|
||||
return <i style={{ width: '25px', height: '25px' }} className="gicon gicon-viewer" />;
|
||||
}
|
||||
|
||||
function ItemDescription({ item }) {
|
||||
if (item.userId) {
|
||||
return <span className="filter-table__weak-italic">(User)</span>;
|
||||
}
|
||||
if (item.teamId) {
|
||||
return <span className="filter-table__weak-italic">(Team)</span>;
|
||||
}
|
||||
return <span className="filter-table__weak-italic">(Role)</span>;
|
||||
}
|
||||
|
||||
export default observer(({ item, removeItem, permissionChanged, itemIndex, folderInfo }) => {
|
||||
const handleRemoveItem = evt => {
|
||||
evt.preventDefault();
|
||||
removeItem(itemIndex);
|
||||
};
|
||||
|
||||
const handleChangePermission = permissionOption => {
|
||||
permissionChanged(itemIndex, permissionOption.value, permissionOption.label);
|
||||
};
|
||||
|
||||
const inheritedFromRoot = item.dashboardId === -1 && !item.inherited;
|
||||
|
||||
return (
|
||||
<tr className={setClassNameHelper(item.inherited)}>
|
||||
<td style={{ width: '1%' }}>
|
||||
<ItemAvatar item={item} />
|
||||
</td>
|
||||
<td style={{ width: '90%' }}>
|
||||
{item.name} <ItemDescription item={item} />
|
||||
</td>
|
||||
<td>
|
||||
{item.inherited &&
|
||||
folderInfo && (
|
||||
<em className="muted no-wrap">
|
||||
Inherited from folder{' '}
|
||||
<a className="text-link" href={`${folderInfo.url}/permissions`}>
|
||||
{folderInfo.title}
|
||||
</a>{' '}
|
||||
</em>
|
||||
)}
|
||||
{inheritedFromRoot && <em className="muted no-wrap">Default Permission</em>}
|
||||
</td>
|
||||
<td className="query-keyword">Can</td>
|
||||
<td>
|
||||
<div className="gf-form">
|
||||
<DescriptionPicker
|
||||
optionsWithDesc={permissionOptions}
|
||||
onSelected={handleChangePermission}
|
||||
value={item.permission}
|
||||
disabled={item.inherited}
|
||||
className={'gf-form-input--form-dropdown-right'}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{!item.inherited ? (
|
||||
<a className="btn btn-danger btn-small" onClick={handleRemoveItem}>
|
||||
<i className="fa fa-remove" />
|
||||
</a>
|
||||
) : (
|
||||
<button className="btn btn-inverse btn-small">
|
||||
<i className="fa fa-lock" />
|
||||
</button>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
@@ -4,17 +4,25 @@ import Tooltip from 'app/core/components/Tooltip/Tooltip';
|
||||
import SlideDown from 'app/core/components/Animations/SlideDown';
|
||||
import { StoreState, FolderInfo } from 'app/types';
|
||||
import { DashboardAcl, PermissionLevel, NewDashboardAclItem } from 'app/types/acl';
|
||||
import { getDashboardPermissions } from '../state/actions';
|
||||
import {
|
||||
getDashboardPermissions,
|
||||
addDashboardPermission,
|
||||
removeDashboardPermission,
|
||||
updateDashboardPermission,
|
||||
} from '../state/actions';
|
||||
import PermissionList from 'app/core/components/PermissionList/PermissionList';
|
||||
import AddPermission from 'app/core/components/PermissionList/AddPermission';
|
||||
import PermissionsInfo from 'app/core/components/Permissions/PermissionsInfo';
|
||||
import PermissionsInfo from 'app/core/components/PermissionList/PermissionsInfo';
|
||||
import { store } from 'app/stores/configureStore';
|
||||
|
||||
export interface Props {
|
||||
dashboardId: number;
|
||||
folder?: FolderInfo;
|
||||
getDashboardPermissions: typeof getDashboardPermissions;
|
||||
permissions: DashboardAcl[];
|
||||
getDashboardPermissions: typeof getDashboardPermissions;
|
||||
updateDashboardPermission: typeof updateDashboardPermission;
|
||||
removeDashboardPermission: typeof removeDashboardPermission;
|
||||
addDashboardPermission: typeof addDashboardPermission;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
@@ -39,15 +47,15 @@ export class DashboardPermissions extends PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
onRemoveItem = (item: DashboardAcl) => {
|
||||
// this.props.removeFolderPermission(item);
|
||||
this.props.removeDashboardPermission(this.props.dashboardId, item);
|
||||
};
|
||||
|
||||
onPermissionChanged = (item: DashboardAcl, level: PermissionLevel) => {
|
||||
// this.props.updateFolderPermission(item, level);
|
||||
this.props.updateDashboardPermission(this.props.dashboardId, item, level);
|
||||
};
|
||||
|
||||
onAddPermission = (newItem: NewDashboardAclItem) => {
|
||||
// return this.props.addFolderPermission(newItem);
|
||||
return this.props.addDashboardPermission(this.props.dashboardId, newItem);
|
||||
};
|
||||
|
||||
onCancelAddPermission = () => {
|
||||
@@ -101,6 +109,9 @@ const mapStateToProps = (state: StoreState) => ({
|
||||
|
||||
const mapDispatchToProps = {
|
||||
getDashboardPermissions,
|
||||
addDashboardPermission,
|
||||
removeDashboardPermission,
|
||||
updateDashboardPermission,
|
||||
};
|
||||
|
||||
export default connectWithStore(DashboardPermissions, mapStateToProps, mapDispatchToProps);
|
||||
|
||||
@@ -68,7 +68,7 @@ export function updateDashboardPermission(
|
||||
itemsToUpdate.push(updated);
|
||||
}
|
||||
|
||||
await getBackendSrv().post(`/api/dashboard/id/${dashboardId}/permissions`, { items: itemsToUpdate });
|
||||
await getBackendSrv().post(`/api/dashboards/id/${dashboardId}/permissions`, { items: itemsToUpdate });
|
||||
await dispatch(getDashboardPermissions(dashboardId));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
import { getLoadingNav } from './state/navModel';
|
||||
import PermissionList from 'app/core/components/PermissionList/PermissionList';
|
||||
import AddPermission from 'app/core/components/PermissionList/AddPermission';
|
||||
import PermissionsInfo from 'app/core/components/Permissions/PermissionsInfo';
|
||||
import PermissionsInfo from 'app/core/components/PermissionList/PermissionsInfo';
|
||||
|
||||
export interface Props {
|
||||
navModel: NavModel;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Action, ActionTypes } from './actions';
|
||||
import { FolderDTO } from 'app/types';
|
||||
import { FolderDTO, OrgRole, PermissionLevel, FolderState } from 'app/types';
|
||||
import { inititalState, folderReducer } from './reducers';
|
||||
|
||||
function getTestFolder(): FolderDTO {
|
||||
@@ -14,29 +14,85 @@ function getTestFolder(): FolderDTO {
|
||||
}
|
||||
|
||||
describe('folder reducer', () => {
|
||||
it('should load folder and set hasChanged to false', () => {
|
||||
const folder = getTestFolder();
|
||||
describe('loadFolder', () => {
|
||||
it('should load folder and set hasChanged to false', () => {
|
||||
const folder = getTestFolder();
|
||||
|
||||
const action: Action = {
|
||||
type: ActionTypes.LoadFolder,
|
||||
payload: folder,
|
||||
};
|
||||
const action: Action = {
|
||||
type: ActionTypes.LoadFolder,
|
||||
payload: folder,
|
||||
};
|
||||
|
||||
const state = folderReducer(inititalState, action);
|
||||
const state = folderReducer(inititalState, action);
|
||||
|
||||
expect(state.hasChanged).toEqual(false);
|
||||
expect(state.title).toEqual('test folder');
|
||||
expect(state.hasChanged).toEqual(false);
|
||||
expect(state.title).toEqual('test folder');
|
||||
});
|
||||
});
|
||||
|
||||
it('should set title', () => {
|
||||
const action: Action = {
|
||||
type: ActionTypes.SetFolderTitle,
|
||||
payload: 'new title',
|
||||
};
|
||||
describe('detFolderTitle', () => {
|
||||
it('should set title', () => {
|
||||
const action: Action = {
|
||||
type: ActionTypes.SetFolderTitle,
|
||||
payload: 'new title',
|
||||
};
|
||||
|
||||
const state = folderReducer(inititalState, action);
|
||||
const state = folderReducer(inititalState, action);
|
||||
|
||||
expect(state.hasChanged).toEqual(true);
|
||||
expect(state.title).toEqual('new title');
|
||||
expect(state.hasChanged).toEqual(true);
|
||||
expect(state.title).toEqual('new title');
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadFolderPermissions', () => {
|
||||
let state: FolderState;
|
||||
|
||||
beforeEach(() => {
|
||||
const action: Action = {
|
||||
type: ActionTypes.LoadFolderPermissions,
|
||||
payload: [
|
||||
{ id: 2, dashboardId: 1, role: OrgRole.Viewer, permission: PermissionLevel.View },
|
||||
{ id: 3, dashboardId: 1, role: OrgRole.Editor, permission: PermissionLevel.Edit },
|
||||
{
|
||||
id: 4,
|
||||
dashboardId: 10,
|
||||
permission: PermissionLevel.View,
|
||||
teamId: 1,
|
||||
team: 'MyTestTeam',
|
||||
inherited: true,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
dashboardId: 1,
|
||||
permission: PermissionLevel.View,
|
||||
userId: 1,
|
||||
userLogin: 'MyTestUser',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
dashboardId: 1,
|
||||
permission: PermissionLevel.Edit,
|
||||
teamId: 2,
|
||||
team: 'MyTestTeam2',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
state = folderReducer(inititalState, action);
|
||||
});
|
||||
|
||||
it('should add permissions to state', async () => {
|
||||
expect(state.permissions.length).toBe(5);
|
||||
expect(state.permissions.length).toBe(5);
|
||||
});
|
||||
|
||||
it('should be sorted by sort rank and alphabetically', async () => {
|
||||
expect(state.permissions[0].name).toBe('MyTestTeam');
|
||||
expect(state.permissions[0].dashboardId).toBe(10);
|
||||
expect(state.permissions[1].name).toBe('Editor');
|
||||
expect(state.permissions[2].name).toBe('Viewer');
|
||||
expect(state.permissions[3].name).toBe('MyTestTeam2');
|
||||
expect(state.permissions[4].name).toBe('MyTestUser');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
export enum OrgRole {
|
||||
Viewer = 'Viewer',
|
||||
Editor = 'Editor',
|
||||
Admin = 'Admin',
|
||||
}
|
||||
|
||||
export interface DashboardAclDTO {
|
||||
id?: number;
|
||||
dashboardId?: number;
|
||||
@@ -7,8 +13,7 @@ export interface DashboardAclDTO {
|
||||
teamId?: number;
|
||||
team?: string;
|
||||
permission?: PermissionLevel;
|
||||
permissionName?: string;
|
||||
role?: string;
|
||||
role?: OrgRole;
|
||||
icon?: string;
|
||||
inherited?: boolean;
|
||||
}
|
||||
@@ -16,7 +21,7 @@ export interface DashboardAclDTO {
|
||||
export interface DashboardAclUpdateDTO {
|
||||
userId: number;
|
||||
teamId: number;
|
||||
role: string;
|
||||
role: OrgRole;
|
||||
permission: PermissionLevel;
|
||||
}
|
||||
|
||||
@@ -29,8 +34,7 @@ export interface DashboardAcl {
|
||||
teamId?: number;
|
||||
team?: string;
|
||||
permission?: PermissionLevel;
|
||||
permissionName?: string;
|
||||
role?: string;
|
||||
role?: OrgRole;
|
||||
icon?: string;
|
||||
name?: string;
|
||||
inherited?: boolean;
|
||||
@@ -46,7 +50,7 @@ export interface DashboardPermissionInfo {
|
||||
export interface NewDashboardAclItem {
|
||||
teamId: number;
|
||||
userId: number;
|
||||
role: string;
|
||||
role: OrgRole;
|
||||
permission: PermissionLevel;
|
||||
type: AclTarget;
|
||||
}
|
||||
@@ -58,10 +62,10 @@ export enum PermissionLevel {
|
||||
}
|
||||
|
||||
export enum AclTarget {
|
||||
Team = 'team',
|
||||
User = 'user',
|
||||
Viewer = 'viewer',
|
||||
Editor = 'editor',
|
||||
Team = 'Team',
|
||||
User = 'User',
|
||||
Viewer = 'Viewer',
|
||||
Editor = 'Editor',
|
||||
}
|
||||
|
||||
export interface AclTargetInfo {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { LocationState, LocationUpdate, UrlQueryMap, UrlQueryValue } from './loc
|
||||
import { NavModel, NavModelItem, NavIndex } from './navModel';
|
||||
import { FolderDTO, FolderState, FolderInfo } from './folder';
|
||||
import { DashboardState } from './dashboard';
|
||||
import { DashboardAcl, OrgRole, PermissionLevel } from './acl';
|
||||
|
||||
export {
|
||||
Team,
|
||||
@@ -24,6 +25,10 @@ export {
|
||||
FolderDTO,
|
||||
FolderState,
|
||||
FolderInfo,
|
||||
DashboardState,
|
||||
DashboardAcl,
|
||||
OrgRole,
|
||||
PermissionLevel,
|
||||
};
|
||||
|
||||
export interface StoreState {
|
||||
|
||||
Reference in New Issue
Block a user