mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
wip: dashboard permissions to redux
This commit is contained in:
parent
d173ebe7e8
commit
c7fdea1dfb
@ -1,24 +0,0 @@
|
||||
import { DashboardAcl } from '../../types';
|
||||
|
||||
export enum ActionTypes {
|
||||
LoadFolderPermissions = 'LoadFolderPermissions',
|
||||
}
|
||||
|
||||
export interface LoadFolderPermissionsAction {
|
||||
type: ActionTypes.LoadFolderPermissions;
|
||||
payload: DashboardAcl[];
|
||||
}
|
||||
|
||||
export type Action = LoadFolderPermissions;
|
||||
|
||||
export const loadFolderPermissions = (items: DashboardAcl[]): LoadFolderPermissionsAction => ({
|
||||
type: ActionTypes.LoadFolderPermissions,
|
||||
payload: items,
|
||||
});
|
||||
|
||||
export function getFolderPermissions(uid: string): ThunkResult<void> {
|
||||
return async dispatch => {
|
||||
const permissions = await backendSrv.get(`/api/folders/${uid}/permissions`);
|
||||
dispatch(loadFolderPermissions(permissions));
|
||||
};
|
||||
}
|
@ -5,7 +5,6 @@ import EmptyListCTA from './components/EmptyListCTA/EmptyListCTA';
|
||||
import { SearchResult } from './components/search/SearchResult';
|
||||
import { TagFilter } from './components/TagFilter/TagFilter';
|
||||
import { SideMenu } from './components/sidemenu/SideMenu';
|
||||
import DashboardPermissions from './components/Permissions/DashboardPermissions';
|
||||
|
||||
export function registerAngularDirectives() {
|
||||
react2AngularDirective('passwordStrength', PasswordStrength, ['password']);
|
||||
@ -18,5 +17,4 @@ export function registerAngularDirectives() {
|
||||
['onSelect', { watchDepth: 'reference' }],
|
||||
['tagOptions', { watchDepth: 'reference' }],
|
||||
]);
|
||||
react2AngularDirective('dashboardPermissions', DashboardPermissions, ['backendSrv', 'dashboardId', 'folder']);
|
||||
}
|
||||
|
31
public/app/core/reducers/processsAclItems.ts
Normal file
31
public/app/core/reducers/processsAclItems.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { DashboardAcl, DashboardAclDTO } from 'app/types/acl';
|
||||
|
||||
export function processAclItems(items: DashboardAclDTO[]): DashboardAcl[] {
|
||||
return items.map(processAclItem).sort((a, b) => b.sortRank - a.sortRank || a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
function processAclItem(dto: DashboardAclDTO): DashboardAcl {
|
||||
const item = dto as DashboardAcl;
|
||||
|
||||
item.sortRank = 0;
|
||||
if (item.userId > 0) {
|
||||
item.name = item.userLogin;
|
||||
item.sortRank = 10;
|
||||
} else if (item.teamId > 0) {
|
||||
item.name = item.team;
|
||||
item.sortRank = 20;
|
||||
} else if (item.role) {
|
||||
item.icon = 'fa fa-fw fa-street-view';
|
||||
item.name = item.role;
|
||||
item.sortRank = 30;
|
||||
if (item.role === 'Editor') {
|
||||
item.sortRank += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (item.inherited) {
|
||||
item.sortRank += 100;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
31
public/app/core/utils/acl.ts
Normal file
31
public/app/core/utils/acl.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { DashboardAcl, DashboardAclDTO } from 'app/types/acl';
|
||||
|
||||
export function processAclItems(items: DashboardAclDTO[]): DashboardAcl[] {
|
||||
return items.map(processAclItem).sort((a, b) => b.sortRank - a.sortRank || a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
function processAclItem(dto: DashboardAclDTO): DashboardAcl {
|
||||
const item = dto as DashboardAcl;
|
||||
|
||||
item.sortRank = 0;
|
||||
if (item.userId > 0) {
|
||||
item.name = item.userLogin;
|
||||
item.sortRank = 10;
|
||||
} else if (item.teamId > 0) {
|
||||
item.name = item.team;
|
||||
item.sortRank = 20;
|
||||
} else if (item.role) {
|
||||
item.icon = 'fa fa-fw fa-street-view';
|
||||
item.name = item.role;
|
||||
item.sortRank = 30;
|
||||
if (item.role === 'Editor') {
|
||||
item.sortRank += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (item.inherited) {
|
||||
item.sortRank += 100;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
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 PermissionList from 'app/core/components/PermissionList/PermissionList';
|
||||
import AddPermission from 'app/core/components/PermissionList/AddPermission';
|
||||
import PermissionsInfo from 'app/core/components/Permissions/PermissionsInfo';
|
||||
import { store } from 'app/stores/configureStore';
|
||||
|
||||
export interface Props {
|
||||
dashboardId: number;
|
||||
folder?: FolderInfo;
|
||||
getDashboardPermissions: typeof getDashboardPermissions;
|
||||
permissions: DashboardAcl[];
|
||||
}
|
||||
|
||||
export interface State {
|
||||
isAdding: boolean;
|
||||
}
|
||||
|
||||
export class DashboardPermissions extends PureComponent<Props, State> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isAdding: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.getDashboardPermissions(this.props.dashboardId);
|
||||
}
|
||||
|
||||
onOpenAddPermissions = () => {
|
||||
this.setState({ isAdding: true });
|
||||
};
|
||||
|
||||
onRemoveItem = (item: DashboardAcl) => {
|
||||
// this.props.removeFolderPermission(item);
|
||||
};
|
||||
|
||||
onPermissionChanged = (item: DashboardAcl, level: PermissionLevel) => {
|
||||
// this.props.updateFolderPermission(item, level);
|
||||
};
|
||||
|
||||
onAddPermission = (newItem: NewDashboardAclItem) => {
|
||||
// return this.props.addFolderPermission(newItem);
|
||||
};
|
||||
|
||||
onCancelAddPermission = () => {
|
||||
this.setState({ isAdding: false });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { permissions, folder } = this.props;
|
||||
const { isAdding } = this.state;
|
||||
console.log('DashboardPermissions', 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.onOpenAddPermissions} disabled={isAdding}>
|
||||
<i className="fa fa-plus" /> Add Permission
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<SlideDown in={isAdding}>
|
||||
<AddPermission onAddPermission={this.onAddPermission} onCancel={this.onCancelAddPermission} />
|
||||
</SlideDown>
|
||||
<PermissionList
|
||||
items={permissions}
|
||||
onRemoveItem={this.onRemoveItem}
|
||||
onPermissionChanged={this.onPermissionChanged}
|
||||
isFetching={false}
|
||||
folderInfo={folder}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function connectWithStore(WrappedComponent, ...args) {
|
||||
const ConnectedWrappedComponent = connect(...args)(WrappedComponent);
|
||||
return props => {
|
||||
return <ConnectedWrappedComponent {...props} store={store} />;
|
||||
};
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: StoreState) => ({
|
||||
permissions: state.dashboard.permissions,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
getDashboardPermissions,
|
||||
};
|
||||
|
||||
export default connectWithStore(DashboardPermissions, mapStateToProps, mapDispatchToProps);
|
@ -30,6 +30,12 @@ import './settings/settings';
|
||||
import './panellinks/module';
|
||||
import './dashlinks/module';
|
||||
|
||||
// angular wrappers
|
||||
import { react2AngularDirective } from 'app/core/utils/react2angular';
|
||||
import DashboardPermissions from './DashboardPermissions/DashboardPermissions';
|
||||
|
||||
react2AngularDirective('dashboardPermissions', DashboardPermissions, ['dashboardId', 'folder']);
|
||||
|
||||
import coreModule from 'app/core/core_module';
|
||||
import { FolderDashboardsCtrl } from './folder_dashboards_ctrl';
|
||||
import { DashboardImportCtrl } from './dashboard_import_ctrl';
|
||||
|
115
public/app/features/dashboard/state/actions.ts
Normal file
115
public/app/features/dashboard/state/actions.ts
Normal file
@ -0,0 +1,115 @@
|
||||
import { StoreState } from 'app/types';
|
||||
import { ThunkAction } from 'redux-thunk';
|
||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||
|
||||
import {
|
||||
DashboardAcl,
|
||||
DashboardAclDTO,
|
||||
PermissionLevel,
|
||||
DashboardAclUpdateDTO,
|
||||
NewDashboardAclItem,
|
||||
} from 'app/types/acl';
|
||||
|
||||
export enum ActionTypes {
|
||||
LoadDashboardPermissions = 'LOAD_DASHBOARD_PERMISSIONS',
|
||||
}
|
||||
|
||||
export interface LoadDashboardPermissionsAction {
|
||||
type: ActionTypes.LoadDashboardPermissions;
|
||||
payload: DashboardAcl[];
|
||||
}
|
||||
|
||||
export type Action = LoadDashboardPermissionsAction;
|
||||
|
||||
type ThunkResult<R> = ThunkAction<R, StoreState, undefined, any>;
|
||||
|
||||
export const loadDashboardPermissions = (items: DashboardAclDTO[]): LoadDashboardPermissionsAction => ({
|
||||
type: ActionTypes.LoadDashboardPermissions,
|
||||
payload: items,
|
||||
});
|
||||
|
||||
export function getDashboardPermissions(id: number): ThunkResult<void> {
|
||||
return async dispatch => {
|
||||
const permissions = await getBackendSrv().get(`/api/dashboards/id/${id}/permissions`);
|
||||
dispatch(loadDashboardPermissions(permissions));
|
||||
};
|
||||
}
|
||||
|
||||
function toUpdateItem(item: DashboardAcl): DashboardAclUpdateDTO {
|
||||
return {
|
||||
userId: item.userId,
|
||||
teamId: item.teamId,
|
||||
role: item.role,
|
||||
permission: item.permission,
|
||||
};
|
||||
}
|
||||
|
||||
export function updateDashboardPermission(
|
||||
dashboardId: number,
|
||||
itemToUpdate: DashboardAcl,
|
||||
level: PermissionLevel
|
||||
): ThunkResult<void> {
|
||||
return async (dispatch, getStore) => {
|
||||
const { dashboard } = getStore();
|
||||
const itemsToUpdate = [];
|
||||
|
||||
for (const item of dashboard.permissions) {
|
||||
if (item.inherited) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const updated = toUpdateItem(itemToUpdate);
|
||||
|
||||
// if this is the item we want to update, update it's permisssion
|
||||
if (itemToUpdate === item) {
|
||||
updated.permission = level;
|
||||
}
|
||||
|
||||
itemsToUpdate.push(updated);
|
||||
}
|
||||
|
||||
await getBackendSrv().post(`/api/dashboard/id/${dashboardId}/permissions`, { items: itemsToUpdate });
|
||||
await dispatch(getDashboardPermissions(dashboardId));
|
||||
};
|
||||
}
|
||||
|
||||
export function removeDashboardPermission(dashboardId: number, itemToDelete: DashboardAcl): ThunkResult<void> {
|
||||
return async (dispatch, getStore) => {
|
||||
const dashboard = getStore().dashboard;
|
||||
const itemsToUpdate = [];
|
||||
|
||||
for (const item of dashboard.permissions) {
|
||||
if (item.inherited || item === itemToDelete) {
|
||||
continue;
|
||||
}
|
||||
itemsToUpdate.push(toUpdateItem(item));
|
||||
}
|
||||
|
||||
await getBackendSrv().post(`/api/dashboards/id/${dashboardId}/permissions`, { items: itemsToUpdate });
|
||||
await dispatch(getDashboardPermissions(dashboardId));
|
||||
};
|
||||
}
|
||||
|
||||
export function addDashboardPermission(dashboardId: number, newItem: NewDashboardAclItem): ThunkResult<void> {
|
||||
return async (dispatch, getStore) => {
|
||||
const { dashboard } = getStore();
|
||||
const itemsToUpdate = [];
|
||||
|
||||
for (const item of dashboard.permissions) {
|
||||
if (item.inherited) {
|
||||
continue;
|
||||
}
|
||||
itemsToUpdate.push(toUpdateItem(item));
|
||||
}
|
||||
|
||||
itemsToUpdate.push({
|
||||
userId: newItem.userId,
|
||||
teamId: newItem.teamId,
|
||||
role: newItem.role,
|
||||
permission: newItem.permission,
|
||||
});
|
||||
|
||||
await getBackendSrv().post(`/api/dashboards/id/${dashboardId}/permissions`, { items: itemsToUpdate });
|
||||
await dispatch(getDashboardPermissions(dashboardId));
|
||||
};
|
||||
}
|
22
public/app/features/dashboard/state/reducers.ts
Normal file
22
public/app/features/dashboard/state/reducers.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { DashboardState } from 'app/types';
|
||||
import { Action, ActionTypes } from './actions';
|
||||
import { processAclItems } from 'app/core/utils/acl';
|
||||
|
||||
export const inititalState: DashboardState = {
|
||||
permissions: [],
|
||||
};
|
||||
|
||||
export const dashboardReducer = (state = inititalState, action: Action): DashboardState => {
|
||||
switch (action.type) {
|
||||
case ActionTypes.LoadDashboardPermissions:
|
||||
return {
|
||||
...state,
|
||||
permissions: processAclItems(action.payload),
|
||||
};
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
export default {
|
||||
dashboard: dashboardReducer,
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
import { FolderState } from 'app/types';
|
||||
import { DashboardAcl, DashboardAclDTO } from 'app/types/acl';
|
||||
import { Action, ActionTypes } from './actions';
|
||||
import { processAclItems } from 'app/core/utils/acl';
|
||||
|
||||
export const inititalState: FolderState = {
|
||||
id: 0,
|
||||
@ -36,36 +36,6 @@ export const folderReducer = (state = inititalState, action: Action): FolderStat
|
||||
return state;
|
||||
};
|
||||
|
||||
function processAclItems(items: DashboardAclDTO[]): DashboardAcl[] {
|
||||
return items.map(processAclItem).sort((a, b) => b.sortRank - a.sortRank || a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
function processAclItem(dto: DashboardAclDTO): DashboardAcl {
|
||||
const item = dto as DashboardAcl;
|
||||
|
||||
item.sortRank = 0;
|
||||
if (item.userId > 0) {
|
||||
item.name = item.userLogin;
|
||||
item.sortRank = 10;
|
||||
} else if (item.teamId > 0) {
|
||||
item.name = item.team;
|
||||
item.sortRank = 20;
|
||||
} else if (item.role) {
|
||||
item.icon = 'fa fa-fw fa-street-view';
|
||||
item.name = item.role;
|
||||
item.sortRank = 30;
|
||||
if (item.role === 'Editor') {
|
||||
item.sortRank += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (item.inherited) {
|
||||
item.sortRank += 100;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
export default {
|
||||
folder: folderReducer,
|
||||
};
|
||||
|
@ -5,12 +5,14 @@ import sharedReducers from 'app/core/reducers';
|
||||
import alertingReducers from 'app/features/alerting/state/reducers';
|
||||
import teamsReducers from 'app/features/teams/state/reducers';
|
||||
import foldersReducers from 'app/features/folders/state/reducers';
|
||||
import dashboardReducers from 'app/features/dashboard/state/reducers';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
...sharedReducers,
|
||||
...alertingReducers,
|
||||
...teamsReducers,
|
||||
...foldersReducers,
|
||||
...dashboardReducers,
|
||||
});
|
||||
|
||||
export let store;
|
||||
|
5
public/app/types/dashboard.ts
Normal file
5
public/app/types/dashboard.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { DashboardAcl } from './acl';
|
||||
|
||||
export interface DashboardState {
|
||||
permissions: DashboardAcl[];
|
||||
}
|
@ -3,6 +3,7 @@ import { AlertRuleDTO, AlertRule, AlertRulesState } from './alerting';
|
||||
import { LocationState, LocationUpdate, UrlQueryMap, UrlQueryValue } from './location';
|
||||
import { NavModel, NavModelItem, NavIndex } from './navModel';
|
||||
import { FolderDTO, FolderState, FolderInfo } from './folder';
|
||||
import { DashboardState } from './dashboard';
|
||||
|
||||
export {
|
||||
Team,
|
||||
@ -32,4 +33,5 @@ export interface StoreState {
|
||||
teams: TeamsState;
|
||||
team: TeamState;
|
||||
folder: FolderState;
|
||||
dashboard: DashboardState;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user