Quellcode durchsuchen

feat: dashboard permissions are working

Torkel Ödegaard vor 7 Jahren
Ursprung
Commit
7bb0109261

+ 2 - 1
public/app/core/components/PermissionList/AddPermission.tsx

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

+ 0 - 0
public/app/core/components/Permissions/PermissionsInfo.tsx → public/app/core/components/PermissionList/PermissionsInfo.tsx


+ 0 - 90
public/app/core/components/Permissions/AddPermissions.test.tsx

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

+ 0 - 128
public/app/core/components/Permissions/AddPermissions.tsx

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

+ 0 - 71
public/app/core/components/Permissions/DashboardPermissions.tsx

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

+ 0 - 43
public/app/core/components/Permissions/DisabledPermissionsListItem.tsx

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

+ 0 - 5
public/app/core/components/Permissions/FolderInfo.ts

@@ -1,5 +0,0 @@
-export interface FolderInfo {
-  id: number;
-  title: string;
-  url: string;
-}

+ 0 - 91
public/app/core/components/Permissions/Permissions.tsx

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

+ 0 - 64
public/app/core/components/Permissions/PermissionsList.tsx

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

+ 0 - 91
public/app/core/components/Permissions/PermissionsListItem.tsx

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

+ 17 - 6
public/app/features/dashboard/DashboardPermissions/DashboardPermissions.tsx

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

+ 1 - 1
public/app/features/dashboard/state/actions.ts

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

+ 1 - 1
public/app/features/folders/FolderPermissions.tsx

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

+ 74 - 18
public/app/features/folders/state/reducers.test.ts

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

+ 14 - 10
public/app/types/acl.ts

@@ -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 {

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

@@ -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 {

+ 3 - 0
scripts/webpack/webpack.common.js

@@ -24,6 +24,9 @@ module.exports = {
       path.resolve('node_modules')
     ],
   },
+  stats: {
+    warningsFilter: /export .* was not found in/
+  },
   node: {
     fs: 'empty',
   },