Commit c7fdea1d by Torkel Ödegaard

wip: dashboard permissions to redux

parent d173ebe7
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'; ...@@ -5,7 +5,6 @@ import EmptyListCTA from './components/EmptyListCTA/EmptyListCTA';
import { SearchResult } from './components/search/SearchResult'; import { SearchResult } from './components/search/SearchResult';
import { TagFilter } from './components/TagFilter/TagFilter'; import { TagFilter } from './components/TagFilter/TagFilter';
import { SideMenu } from './components/sidemenu/SideMenu'; import { SideMenu } from './components/sidemenu/SideMenu';
import DashboardPermissions from './components/Permissions/DashboardPermissions';
export function registerAngularDirectives() { export function registerAngularDirectives() {
react2AngularDirective('passwordStrength', PasswordStrength, ['password']); react2AngularDirective('passwordStrength', PasswordStrength, ['password']);
...@@ -18,5 +17,4 @@ export function registerAngularDirectives() { ...@@ -18,5 +17,4 @@ export function registerAngularDirectives() {
['onSelect', { watchDepth: 'reference' }], ['onSelect', { watchDepth: 'reference' }],
['tagOptions', { watchDepth: 'reference' }], ['tagOptions', { watchDepth: 'reference' }],
]); ]);
react2AngularDirective('dashboardPermissions', DashboardPermissions, ['backendSrv', 'dashboardId', 'folder']);
} }
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;
}
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;
}
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'; ...@@ -30,6 +30,12 @@ import './settings/settings';
import './panellinks/module'; import './panellinks/module';
import './dashlinks/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 coreModule from 'app/core/core_module';
import { FolderDashboardsCtrl } from './folder_dashboards_ctrl'; import { FolderDashboardsCtrl } from './folder_dashboards_ctrl';
import { DashboardImportCtrl } from './dashboard_import_ctrl'; import { DashboardImportCtrl } from './dashboard_import_ctrl';
......
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));
};
}
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,
};
import { FolderState } from 'app/types'; import { FolderState } from 'app/types';
import { DashboardAcl, DashboardAclDTO } from 'app/types/acl';
import { Action, ActionTypes } from './actions'; import { Action, ActionTypes } from './actions';
import { processAclItems } from 'app/core/utils/acl';
export const inititalState: FolderState = { export const inititalState: FolderState = {
id: 0, id: 0,
...@@ -36,36 +36,6 @@ export const folderReducer = (state = inititalState, action: Action): FolderStat ...@@ -36,36 +36,6 @@ export const folderReducer = (state = inititalState, action: Action): FolderStat
return state; 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 { export default {
folder: folderReducer, folder: folderReducer,
}; };
...@@ -5,12 +5,14 @@ import sharedReducers from 'app/core/reducers'; ...@@ -5,12 +5,14 @@ import sharedReducers from 'app/core/reducers';
import alertingReducers from 'app/features/alerting/state/reducers'; import alertingReducers from 'app/features/alerting/state/reducers';
import teamsReducers from 'app/features/teams/state/reducers'; import teamsReducers from 'app/features/teams/state/reducers';
import foldersReducers from 'app/features/folders/state/reducers'; import foldersReducers from 'app/features/folders/state/reducers';
import dashboardReducers from 'app/features/dashboard/state/reducers';
const rootReducer = combineReducers({ const rootReducer = combineReducers({
...sharedReducers, ...sharedReducers,
...alertingReducers, ...alertingReducers,
...teamsReducers, ...teamsReducers,
...foldersReducers, ...foldersReducers,
...dashboardReducers,
}); });
export let store; export let store;
......
import { DashboardAcl } from './acl';
export interface DashboardState {
permissions: DashboardAcl[];
}
...@@ -3,6 +3,7 @@ import { AlertRuleDTO, AlertRule, AlertRulesState } from './alerting'; ...@@ -3,6 +3,7 @@ import { AlertRuleDTO, AlertRule, AlertRulesState } from './alerting';
import { LocationState, LocationUpdate, UrlQueryMap, UrlQueryValue } from './location'; import { LocationState, LocationUpdate, UrlQueryMap, UrlQueryValue } from './location';
import { NavModel, NavModelItem, NavIndex } from './navModel'; import { NavModel, NavModelItem, NavIndex } from './navModel';
import { FolderDTO, FolderState, FolderInfo } from './folder'; import { FolderDTO, FolderState, FolderInfo } from './folder';
import { DashboardState } from './dashboard';
export { export {
Team, Team,
...@@ -32,4 +33,5 @@ export interface StoreState { ...@@ -32,4 +33,5 @@ export interface StoreState {
teams: TeamsState; teams: TeamsState;
team: TeamState; team: TeamState;
folder: FolderState; folder: FolderState;
dashboard: DashboardState;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment