Redone with DataSourcePermissions

This commit is contained in:
Peter Holmberg 2018-10-09 16:05:40 +02:00
parent 78b56dd528
commit d71ae7bd4d
7 changed files with 255 additions and 18 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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