mirror of
https://github.com/grafana/grafana.git
synced 2026-07-30 00:08:10 -05:00
wip: dashboard permissions to redux
This commit is contained in:
@@ -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 { 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() {
|
|||||||
['onSelect', { watchDepth: 'reference' }],
|
['onSelect', { watchDepth: 'reference' }],
|
||||||
['tagOptions', { watchDepth: 'reference' }],
|
['tagOptions', { watchDepth: 'reference' }],
|
||||||
]);
|
]);
|
||||||
react2AngularDirective('dashboardPermissions', DashboardPermissions, ['backendSrv', 'dashboardId', 'folder']);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,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 './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';
|
||||||
|
|||||||
@@ -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));
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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 { 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
|
|||||||
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';
|
|||||||
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;
|
||||||
|
|||||||
@@ -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 { 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 {
|
|||||||
teams: TeamsState;
|
teams: TeamsState;
|
||||||
team: TeamState;
|
team: TeamState;
|
||||||
folder: FolderState;
|
folder: FolderState;
|
||||||
|
dashboard: DashboardState;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user