Просмотр исходного кода

Redone with DataSourcePermissions

Peter Holmberg 7 лет назад
Родитель
Сommit
d71ae7bd4d

+ 128 - 0
public/app/features/datasources/AddDataSourcePermissions.tsx

@@ -0,0 +1,128 @@
+import React, { PureComponent } from 'react';
+import { UserPicker } from 'app/core/components/Picker/UserPicker';
+import { Team, TeamPicker } from 'app/core/components/Picker/TeamPicker';
+import DescriptionPicker, { OptionWithDescription } from 'app/core/components/Picker/DescriptionPicker';
+import { AclTarget, DataSourcePermissionLevel } from 'app/types/acl';
+import { User } from 'app/types';
+
+export interface Props {
+  onAddPermission: (state) => void;
+  onCancel: () => void;
+}
+
+interface State {
+  userId: number;
+  teamId: number;
+  type: AclTarget;
+  permission: DataSourcePermissionLevel;
+}
+
+export class AddDataSourcePermissions extends PureComponent<Props, State> {
+  cleanState = () => ({
+    userId: 0,
+    teamId: 0,
+    type: AclTarget.Team,
+    permission: DataSourcePermissionLevel.Query,
+  });
+
+  state = this.cleanState();
+
+  isValid() {
+    switch (this.state.type) {
+      case AclTarget.Team:
+        return this.state.teamId > 0;
+      case AclTarget.User:
+        return this.state.userId > 0;
+    }
+    return true;
+  }
+
+  onTeamSelected = (team: Team) => {
+    this.setState({ teamId: team ? team.id : 0 });
+  };
+
+  onUserSelected = (user: User) => {
+    this.setState({ userId: user ? user.id : 0 });
+  };
+
+  onPermissionChanged = (permission: OptionWithDescription) => {
+    this.setState({ permission: permission.value });
+  };
+
+  onTypeChanged = event => {
+    const type = event.target.value as AclTarget;
+
+    this.setState({ type: type, userId: 0, teamId: 0 });
+  };
+
+  onSubmit = async event => {
+    event.preventDefault();
+
+    await this.props.onAddPermission(this.state);
+    this.setState(this.cleanState());
+  };
+
+  render() {
+    const { onCancel } = this.props;
+    const { type, teamId, userId, permission } = this.state;
+
+    const pickerClassName = 'width-20';
+    const aclTargets = [{ value: AclTarget.Team, text: 'Team' }, { value: AclTarget.User, text: 'User' }];
+    const permissionLevels = [
+      { value: DataSourcePermissionLevel.Query, label: 'Query', description: 'Can query data source.' },
+    ];
+
+    return (
+      <div className="gf-form-inline cta-form">
+        <button className="cta-form__close btn btn-transparent" onClick={onCancel}>
+          <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={type} onChange={this.onTypeChanged}>
+                  {aclTargets.map((option, idx) => {
+                    return (
+                      <option key={idx} value={option.value}>
+                        {option.text}
+                      </option>
+                    );
+                  })}
+                </select>
+              </div>
+            </div>
+            {type === AclTarget.User && (
+              <div className="gf-form">
+                <UserPicker onSelected={this.onUserSelected} value={userId.toString()} className={pickerClassName} />
+              </div>
+            )}
+
+            {type === AclTarget.Team && (
+              <div className="gf-form">
+                <TeamPicker onSelected={this.onTeamSelected} value={teamId.toString()} className={pickerClassName} />
+              </div>
+            )}
+            <div className="gf-form">
+              <DescriptionPicker
+                optionsWithDesc={permissionLevels}
+                onSelected={this.onPermissionChanged}
+                value={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={!this.isValid()}>
+                Save
+              </button>
+            </div>
+          </div>
+        </form>
+      </div>
+    );
+  }
+}
+
+export default AddDataSourcePermissions;

+ 63 - 15
public/app/features/datasources/DataSourcePermissions.tsx

@@ -1,10 +1,20 @@
 import React, { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import SlideDown from '../../core/components/Animations/SlideDown';
-import AddPermissions from '../../core/components/PermissionList/AddPermission';
-import { AclTarget, AclTargetInfo } from 'app/types/acl';
+import AddDataSourcePermissions from './AddDataSourcePermissions';
+import { AclTarget } from 'app/types/acl';
+import { addDataSourcePermission, loadDataSourcePermissions, removeDataSourcePermission } from './state/actions';
+import { DashboardAcl, DataSourcePermission } from 'app/types';
+import { getRouteParamsId } from '../../core/selectors/location';
+import PermissionList from '../../core/components/PermissionList/PermissionList';
 
-export interface Props {}
+export interface Props {
+  dataSourcePermissions: DataSourcePermission[];
+  pageId: number;
+  addDataSourcePermission: typeof addDataSourcePermission;
+  loadDataSourcePermissions: typeof loadDataSourcePermissions;
+  removeDataSourcePermission: typeof removeDataSourcePermission;
+}
 
 interface State {
   isAdding: boolean;
@@ -15,13 +25,42 @@ export class DataSourcePermissions extends PureComponent<Props, State> {
     isAdding: false,
   };
 
+  componentDidMount() {
+    this.fetchDataSourcePermissions();
+  }
+
+  async fetchDataSourcePermissions() {
+    const { pageId, loadDataSourcePermissions } = this.props;
+
+    return await loadDataSourcePermissions(pageId);
+  }
+
   onOpenAddPermissions = () => {
     this.setState({
       isAdding: true,
     });
   };
 
-  onAddPermission = () => {};
+  onAddPermission = state => {
+    const { pageId, addDataSourcePermission } = this.props;
+    const data = {
+      permission: state.permission,
+      userId: 0,
+      teamId: 0,
+    };
+
+    if (state.type === AclTarget.Team) {
+      data.teamId = state.teamId;
+    } else if (state.team === AclTarget.User) {
+      data.userId = state.userId;
+    }
+
+    addDataSourcePermission(pageId, data);
+  };
+
+  onRemovePermission = (item: DashboardAcl) => {
+    this.props.removeDataSourcePermission(1, 1);
+  };
 
   onCancelAddPermission = () => {
     this.setState({
@@ -30,13 +69,9 @@ export class DataSourcePermissions extends PureComponent<Props, State> {
   };
 
   render() {
+    const { dataSourcePermissions } = this.props;
     const { isAdding } = this.state;
 
-    const dashboardAclTargets: AclTargetInfo[] = [
-      { value: AclTarget.Team, text: 'Team' },
-      { value: AclTarget.User, text: 'User' },
-    ];
-
     return (
       <div>
         <div className="page-action-bar">
@@ -47,20 +82,33 @@ export class DataSourcePermissions extends PureComponent<Props, State> {
           </button>
         </div>
         <SlideDown in={isAdding}>
-          <AddPermissions
-            dashboardAclTargets={dashboardAclTargets}
-            showPermissionLevels={false}
-            onAddPermission={this.onAddPermission}
+          <AddDataSourcePermissions
+            onAddPermission={state => this.onAddPermission(state)}
             onCancel={this.onCancelAddPermission}
           />
         </SlideDown>
+        <PermissionList
+          items={dataSourcePermissions}
+          onRemoveItem={this.onRemovePermission}
+          onPermissionChanged={() => {}}
+          isFetching={false}
+        />
       </div>
     );
   }
 }
 
 function mapStateToProps(state) {
-  return {};
+  return {
+    pageId: getRouteParamsId(state.location),
+    dataSourcePermissions: state.dataSources.dataSourcePermissions,
+  };
 }
 
-export default connect(mapStateToProps)(DataSourcePermissions);
+const mapDispatchToProps = {
+  addDataSourcePermission,
+  loadDataSourcePermissions,
+  removeDataSourcePermission,
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(DataSourcePermissions);

+ 38 - 1
public/app/features/datasources/state/actions.ts

@@ -5,12 +5,14 @@ import { LayoutMode } from '../../../core/components/LayoutSelector/LayoutSelect
 import { updateLocation, updateNavIndex, UpdateNavIndexAction } from '../../../core/actions';
 import { UpdateLocationAction } from '../../../core/actions/location';
 import { buildNavModel } from './navModel';
+import { DataSourcePermission } from '../../../types/datasources';
 
 export enum ActionTypes {
   LoadDataSources = 'LOAD_DATA_SOURCES',
   LoadDataSourceTypes = 'LOAD_DATA_SOURCE_TYPES',
   LoadDataSource = 'LOAD_DATA_SOURCE',
   LoadDataSourceMeta = 'LOAD_DATA_SOURCE_META',
+  LoadDataSourcePermissions = 'LOAD_DATA_SOURCE_PERMISSIONS',
   SetDataSourcesSearchQuery = 'SET_DATA_SOURCES_SEARCH_QUERY',
   SetDataSourcesLayoutMode = 'SET_DATA_SOURCES_LAYOUT_MODE',
   SetDataSourceTypeSearchQuery = 'SET_DATA_SOURCE_TYPE_SEARCH_QUERY',
@@ -51,6 +53,11 @@ export interface LoadDataSourceMetaAction {
   payload: Plugin;
 }
 
+export interface LoadDataSourcePermissionsAction {
+  type: ActionTypes.LoadDataSourcePermissions;
+  payload: DataSourcePermission[];
+}
+
 const dataSourcesLoaded = (dataSources: DataSource[]): LoadDataSourcesAction => ({
   type: ActionTypes.LoadDataSources,
   payload: dataSources,
@@ -71,6 +78,13 @@ const dataSourceTypesLoaded = (dataSourceTypes: Plugin[]): LoadDataSourceTypesAc
   payload: dataSourceTypes,
 });
 
+const dataSourcePermissionsLoaded = (
+  dataSourcePermissions: DataSourcePermission[]
+): LoadDataSourcePermissionsAction => ({
+  type: ActionTypes.LoadDataSourcePermissions,
+  payload: dataSourcePermissions,
+});
+
 export const setDataSourcesSearchQuery = (searchQuery: string): SetDataSourcesSearchQueryAction => ({
   type: ActionTypes.SetDataSourcesSearchQuery,
   payload: searchQuery,
@@ -95,7 +109,8 @@ export type Action =
   | SetDataSourceTypeSearchQueryAction
   | LoadDataSourceAction
   | UpdateNavIndexAction
-  | LoadDataSourceMetaAction;
+  | LoadDataSourceMetaAction
+  | LoadDataSourcePermissionsAction;
 
 type ThunkResult<R> = ThunkAction<R, StoreState, undefined, Action>;
 
@@ -145,6 +160,28 @@ export function loadDataSourceTypes(): ThunkResult<void> {
   };
 }
 
+export function loadDataSourcePermissions(id: number): ThunkResult<void> {
+  return async dispatch => {
+    const response = await getBackendSrv().get(`/api/datasources/${id}/permissions`);
+    dispatch(dataSourcePermissionsLoaded(response.permissions));
+  };
+}
+
+export function addDataSourcePermission(id: number, data: object): ThunkResult<void> {
+  return async dispatch => {
+    await getBackendSrv().post(`/api/datasources/${id}/permissions`, data);
+
+    dispatch(loadDataSourcePermissions(id));
+  };
+}
+
+export function removeDataSourcePermission(id: number, permissionId: number): ThunkResult<void> {
+  return async dispatch => {
+    await getBackendSrv().delete(`/api/datasources/${id}/permissions/${permissionId}`);
+    dispatch(loadDataSourcePermissions(id));
+  };
+}
+
 export function nameExits(dataSources, name) {
   return (
     dataSources.filter(dataSource => {

+ 5 - 1
public/app/features/datasources/state/reducers.ts

@@ -1,4 +1,4 @@
-import { DataSource, DataSourcesState, Plugin } from 'app/types';
+import { DataSource, DataSourcePermission, DataSourcesState, Plugin } from 'app/types';
 import { Action, ActionTypes } from './actions';
 import { LayoutModes } from '../../../core/components/LayoutSelector/LayoutSelector';
 
@@ -11,6 +11,7 @@ const initialState: DataSourcesState = {
   dataSourceTypes: [] as Plugin[],
   dataSourceTypeSearchQuery: '',
   dataSourceMeta: {} as Plugin,
+  dataSourcePermissions: [] as DataSourcePermission[],
 };
 
 export const dataSourcesReducer = (state = initialState, action: Action): DataSourcesState => {
@@ -35,6 +36,9 @@ export const dataSourcesReducer = (state = initialState, action: Action): DataSo
 
     case ActionTypes.LoadDataSourceMeta:
       return { ...state, dataSourceMeta: action.payload };
+
+    case ActionTypes.LoadDataSourcePermissions:
+      return { ...state, dataSourcePermissions: action.payload };
   }
 
   return state;

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

@@ -61,6 +61,11 @@ export enum PermissionLevel {
   Admin = 4,
 }
 
+export enum DataSourcePermissionLevel {
+  Query = 1,
+  Admin = 2,
+}
+
 export enum AclTarget {
   Team = 'Team',
   User = 'User',

+ 14 - 0
public/app/types/datasources.ts

@@ -1,6 +1,19 @@
 import { LayoutMode } from '../core/components/LayoutSelector/LayoutSelector';
 import { Plugin } from './plugins';
 
+export interface DataSourcePermission {
+  id: number;
+  datasourceId: number;
+  userId: number;
+  userLogin: string;
+  userEmail: string;
+  userAvatarUrl: string;
+  permission: number;
+  permissionName: string;
+  created: string;
+  updated: string;
+}
+
 export interface DataSource {
   id: number;
   orgId: number;
@@ -27,4 +40,5 @@ export interface DataSourcesState {
   dataSourceTypes: Plugin[];
   dataSource: DataSource;
   dataSourceMeta: Plugin;
+  dataSourcePermissions: DataSourcePermission[];
 }

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

@@ -7,7 +7,7 @@ import { DashboardState } from './dashboard';
 import { DashboardAcl, OrgRole, PermissionLevel } from './acl';
 import { ApiKey, ApiKeysState, NewApiKey } from './apiKeys';
 import { Invitee, OrgUser, User, UsersState } from './user';
-import { DataSource, DataSourcesState } from './datasources';
+import { DataSource, DataSourcePermission, DataSourcesState } from './datasources';
 import { PluginMeta, Plugin, PluginsState } from './plugins';
 
 export {
@@ -41,6 +41,7 @@ export {
   Plugin,
   PluginsState,
   DataSourcesState,
+  DataSourcePermission,
   Invitee,
   OrgUser,
   User,