Ver Fonte

wip: dashboard permissions to redux

Torkel Ödegaard há 7 anos atrás
pai
commit
c7fdea1dfb

+ 0 - 24
public/app/core/actions/permissions.ts

@@ -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));
-  };
-}

+ 0 - 2
public/app/core/angular_wrappers.ts

@@ -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 - 0
public/app/core/reducers/processsAclItems.ts

@@ -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 - 0
public/app/core/utils/acl.ts

@@ -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;
+}

+ 106 - 0
public/app/features/dashboard/DashboardPermissions/DashboardPermissions.tsx

@@ -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);

+ 6 - 0
public/app/features/dashboard/all.ts

@@ -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 - 0
public/app/features/dashboard/state/actions.ts

@@ -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 - 0
public/app/features/dashboard/state/reducers.ts

@@ -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 - 31
public/app/features/folders/state/reducers.ts

@@ -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,
 };

+ 2 - 0
public/app/stores/configureStore.ts

@@ -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 - 0
public/app/types/dashboard.ts

@@ -0,0 +1,5 @@
+import { DashboardAcl } from './acl';
+
+export interface DashboardState {
+  permissions: DashboardAcl[];
+}

+ 2 - 0
public/app/types/index.ts

@@ -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;
 }