Merge pull request #13700 from grafana/permissions-code-to-enterprise
Permissions code to enterprise
This commit is contained in:
@@ -1,29 +0,0 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { AddDataSourcePermissions, Props } from './AddDataSourcePermissions';
|
||||
import { AclTarget } from '../../types/acl';
|
||||
|
||||
const setup = () => {
|
||||
const props: Props = {
|
||||
onAddPermission: jest.fn(),
|
||||
onCancel: jest.fn(),
|
||||
};
|
||||
|
||||
return shallow(<AddDataSourcePermissions {...props} />);
|
||||
};
|
||||
|
||||
describe('Render', () => {
|
||||
it('should render component', () => {
|
||||
const wrapper = setup();
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render user picker', () => {
|
||||
const wrapper = setup();
|
||||
|
||||
wrapper.instance().setState({ type: AclTarget.User });
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -1,77 +0,0 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { DataSourcePermissions, Props } from './DataSourcePermissions';
|
||||
import { DataSourcePermission, DataSourcePermissionDTO } from 'app/types';
|
||||
import { AclTarget, dashboardPermissionLevels } from '../../types/acl';
|
||||
|
||||
const setup = (propOverrides?: object) => {
|
||||
const props: Props = {
|
||||
dataSourcePermission: {} as DataSourcePermissionDTO,
|
||||
pageId: 1,
|
||||
addDataSourcePermission: jest.fn(),
|
||||
enableDataSourcePermissions: jest.fn(),
|
||||
disableDataSourcePermissions: jest.fn(),
|
||||
loadDataSourcePermissions: jest.fn(),
|
||||
removeDataSourcePermission: jest.fn(),
|
||||
};
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
|
||||
const wrapper = shallow(<DataSourcePermissions {...props} />);
|
||||
const instance = wrapper.instance() as DataSourcePermissions;
|
||||
|
||||
return {
|
||||
wrapper,
|
||||
instance,
|
||||
};
|
||||
};
|
||||
|
||||
describe('Render', () => {
|
||||
it('should render component', () => {
|
||||
const { wrapper } = setup();
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render permissions enabled', () => {
|
||||
const { wrapper } = setup({
|
||||
dataSourcePermission: {
|
||||
enabled: true,
|
||||
datasourceId: 1,
|
||||
permissions: [] as DataSourcePermission[],
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Functions', () => {
|
||||
describe('on add permissions', () => {
|
||||
const { instance } = setup();
|
||||
|
||||
it('should add permissions for team', () => {
|
||||
const mockState = {
|
||||
permission: dashboardPermissionLevels[0].value,
|
||||
teamId: 1,
|
||||
type: AclTarget.Team,
|
||||
};
|
||||
|
||||
instance.onAddPermission(mockState);
|
||||
|
||||
expect(instance.props.addDataSourcePermission).toHaveBeenCalledWith(1, { teamId: 1, permission: 1 });
|
||||
});
|
||||
|
||||
it('should add permissions for user', () => {
|
||||
const mockState = {
|
||||
permission: dashboardPermissionLevels[0].value,
|
||||
userId: 1,
|
||||
type: AclTarget.User,
|
||||
};
|
||||
|
||||
instance.onAddPermission(mockState);
|
||||
|
||||
expect(instance.props.addDataSourcePermission).toHaveBeenCalledWith(1, { userId: 1, permission: 1 });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,155 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import SlideDown from '../../core/components/Animations/SlideDown';
|
||||
import AddDataSourcePermissions from './AddDataSourcePermissions';
|
||||
import DataSourcePermissionsList from './DataSourcePermissionsList';
|
||||
import { AclTarget } from 'app/types/acl';
|
||||
import {
|
||||
addDataSourcePermission,
|
||||
disableDataSourcePermissions,
|
||||
enableDataSourcePermissions,
|
||||
loadDataSourcePermissions,
|
||||
removeDataSourcePermission,
|
||||
} from './state/actions';
|
||||
import { DataSourcePermissionDTO } from 'app/types';
|
||||
import { getRouteParamsId } from '../../core/selectors/location';
|
||||
|
||||
export interface Props {
|
||||
dataSourcePermission: DataSourcePermissionDTO;
|
||||
pageId: number;
|
||||
addDataSourcePermission: typeof addDataSourcePermission;
|
||||
enableDataSourcePermissions: typeof enableDataSourcePermissions;
|
||||
disableDataSourcePermissions: typeof disableDataSourcePermissions;
|
||||
loadDataSourcePermissions: typeof loadDataSourcePermissions;
|
||||
removeDataSourcePermission: typeof removeDataSourcePermission;
|
||||
}
|
||||
|
||||
interface State {
|
||||
isAdding: boolean;
|
||||
}
|
||||
|
||||
export class DataSourcePermissions extends PureComponent<Props, State> {
|
||||
state = {
|
||||
isAdding: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchDataSourcePermissions();
|
||||
}
|
||||
|
||||
async fetchDataSourcePermissions() {
|
||||
const { pageId, loadDataSourcePermissions } = this.props;
|
||||
|
||||
return await loadDataSourcePermissions(pageId);
|
||||
}
|
||||
|
||||
onOpenAddPermissions = () => {
|
||||
this.setState({
|
||||
isAdding: true,
|
||||
});
|
||||
};
|
||||
|
||||
onEnablePermissions = () => {
|
||||
const { pageId, enableDataSourcePermissions } = this.props;
|
||||
enableDataSourcePermissions(pageId);
|
||||
};
|
||||
|
||||
onDisablePermissions = () => {
|
||||
const { pageId, disableDataSourcePermissions } = this.props;
|
||||
|
||||
disableDataSourcePermissions(pageId);
|
||||
};
|
||||
|
||||
onAddPermission = state => {
|
||||
const { pageId, addDataSourcePermission } = this.props;
|
||||
const data = {
|
||||
permission: state.permission,
|
||||
};
|
||||
|
||||
if (state.type === AclTarget.Team) {
|
||||
addDataSourcePermission(pageId, Object.assign(data, { teamId: state.teamId }));
|
||||
} else if (state.type === AclTarget.User) {
|
||||
addDataSourcePermission(pageId, Object.assign(data, { userId: state.userId }));
|
||||
}
|
||||
};
|
||||
|
||||
onRemovePermission = item => {
|
||||
this.props.removeDataSourcePermission(item.datasourceId, item.id);
|
||||
};
|
||||
|
||||
onCancelAddPermission = () => {
|
||||
this.setState({
|
||||
isAdding: false,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { dataSourcePermission } = this.props;
|
||||
const { isAdding } = this.state;
|
||||
const isPermissionsEnabled = dataSourcePermission.enabled;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="page-action-bar">
|
||||
<h3 className="page-sub-heading">Permissions</h3>
|
||||
<div className="page-action-bar__spacer" />
|
||||
{isPermissionsEnabled && [
|
||||
<button
|
||||
key="add-permission"
|
||||
className="btn btn-success pull-right"
|
||||
onClick={this.onOpenAddPermissions}
|
||||
disabled={isAdding}
|
||||
>
|
||||
<i className="fa fa-plus" /> Add Permission
|
||||
</button>,
|
||||
<button key="disable-permissions" className="btn btn-danger pull-right" onClick={this.onDisablePermissions}>
|
||||
Disable Permissions
|
||||
</button>,
|
||||
]}
|
||||
</div>
|
||||
{!isPermissionsEnabled ? (
|
||||
<div className="empty-list-cta">
|
||||
<div className="empty-list-cta__title">{'Permissions not enabled for this data source.'}</div>
|
||||
<button onClick={this.onEnablePermissions} className="empty-list-cta__button btn btn-xlarge btn-success">
|
||||
{'Enable'}
|
||||
</button>
|
||||
<div className="empty-list-cta__pro-tip">
|
||||
<i className="fa fa-rocket" /> ProTip:{' '}
|
||||
{'Only admins will be able to query the data source after you enable permissions.'}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<SlideDown in={isAdding}>
|
||||
<AddDataSourcePermissions
|
||||
onAddPermission={state => this.onAddPermission(state)}
|
||||
onCancel={this.onCancelAddPermission}
|
||||
/>
|
||||
</SlideDown>
|
||||
<DataSourcePermissionsList
|
||||
items={dataSourcePermission.permissions}
|
||||
onRemoveItem={item => this.onRemovePermission(item)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
pageId: getRouteParamsId(state.location),
|
||||
dataSourcePermission: state.dataSources.dataSourcePermission,
|
||||
};
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
addDataSourcePermission,
|
||||
enableDataSourcePermissions,
|
||||
disableDataSourcePermissions,
|
||||
loadDataSourcePermissions,
|
||||
removeDataSourcePermission,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(DataSourcePermissions);
|
||||
@@ -1,32 +0,0 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { DataSourcePermissionsList, Props } from './DataSourcePermissionsList';
|
||||
import { DataSourcePermission } from '../../types';
|
||||
import { getMockDataSourcePermissionsTeam, getMockDataSourcePermissionsUser } from './__mocks__/dataSourcesMocks';
|
||||
|
||||
const setup = (propOverrides?: object) => {
|
||||
const props: Props = {
|
||||
items: [] as DataSourcePermission[],
|
||||
onRemoveItem: jest.fn(),
|
||||
};
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
|
||||
return shallow(<DataSourcePermissionsList {...props} />);
|
||||
};
|
||||
|
||||
describe('Render', () => {
|
||||
it('should render component', () => {
|
||||
const wrapper = setup();
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render items', () => {
|
||||
const wrapper = setup({
|
||||
items: [getMockDataSourcePermissionsUser(), getMockDataSourcePermissionsTeam()],
|
||||
});
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -1,109 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { DataSourcePermission } from 'app/types';
|
||||
import { dataSourceAclLevels, DataSourcePermissionLevel } from 'app/types/acl';
|
||||
import DescriptionPicker from '../../core/components/Picker/DescriptionPicker';
|
||||
|
||||
export interface Props {
|
||||
items: DataSourcePermission[];
|
||||
onRemoveItem: (item) => void;
|
||||
}
|
||||
|
||||
export class DataSourcePermissionsList extends PureComponent<Props> {
|
||||
renderAvatar(item) {
|
||||
if (item.teamId) {
|
||||
return <img className="filter-table__avatar" src={item.teamAvatarUrl} />;
|
||||
} else if (item.userId) {
|
||||
return <img className="filter-table__avatar" src={item.userAvatarUrl} />;
|
||||
}
|
||||
|
||||
return <i style={{ width: '25px', height: '25px' }} className="gicon gicon-viewer" />;
|
||||
}
|
||||
|
||||
renderDescription(item) {
|
||||
if (item.userId) {
|
||||
return [
|
||||
<span key="name">{item.userLogin} </span>,
|
||||
<span key="description" className="filter-table__weak-italic">
|
||||
(User)
|
||||
</span>,
|
||||
];
|
||||
}
|
||||
if (item.teamId) {
|
||||
return [
|
||||
<span key="name">{item.team} </span>,
|
||||
<span key="description" className="filter-table__weak-italic">
|
||||
(Team)
|
||||
</span>,
|
||||
];
|
||||
}
|
||||
return <span className="filter-table__weak-italic">(Role)</span>;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { items } = this.props;
|
||||
const permissionLevels = [...dataSourceAclLevels];
|
||||
permissionLevels.push({ value: DataSourcePermissionLevel.Admin, label: 'Admin', description: '' });
|
||||
|
||||
return (
|
||||
<table className="filter-table gf-form-group">
|
||||
<tbody>
|
||||
<tr className="gf-form-disabled">
|
||||
<td style={{ width: '1%' }}>
|
||||
<i style={{ width: '25px', height: '25px' }} className="gicon gicon-shield" />
|
||||
</td>
|
||||
<td style={{ width: '90%' }}>
|
||||
Admin
|
||||
<span className="filter-table__weak-italic"> (Role)</span>
|
||||
</td>
|
||||
<td />
|
||||
<td className="query-keyword">Can</td>
|
||||
<td>
|
||||
<div className="gf-form">
|
||||
<DescriptionPicker
|
||||
optionsWithDesc={permissionLevels}
|
||||
onSelected={() => {}}
|
||||
value={2}
|
||||
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>
|
||||
{items.map((item, index) => {
|
||||
return (
|
||||
<tr key={`${item.id}-${index}`}>
|
||||
<td style={{ width: '1%' }}>{this.renderAvatar(item)}</td>
|
||||
<td style={{ width: '90%' }}>{this.renderDescription(item)}</td>
|
||||
<td />
|
||||
<td className="query-keyword">Can</td>
|
||||
<td>
|
||||
<div className="gf-form">
|
||||
<DescriptionPicker
|
||||
optionsWithDesc={permissionLevels}
|
||||
onSelected={() => {}}
|
||||
value={1}
|
||||
disabled={true}
|
||||
className={'gf-form-input--form-dropdown-right'}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button className="btn btn-danger btn-small" onClick={() => this.props.onRemoveItem(item)}>
|
||||
<i className="fa fa-remove" />
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DataSourcePermissionsList;
|
||||
@@ -1,84 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { hot } from 'react-hot-loader';
|
||||
import { connect } from 'react-redux';
|
||||
import PageHeader from '../../core/components/PageHeader/PageHeader';
|
||||
import DataSourcePermissions from './DataSourcePermissions';
|
||||
import { DataSource, NavModel } from 'app/types';
|
||||
import { loadDataSource } from './state/actions';
|
||||
import { getNavModel } from '../../core/selectors/navModel';
|
||||
import { getRouteParamsId, getRouteParamsPage } from '../../core/selectors/location';
|
||||
import { getDataSourceLoadingNav } from './state/navModel';
|
||||
import { getDataSource } from './state/selectors';
|
||||
|
||||
export interface Props {
|
||||
navModel: NavModel;
|
||||
dataSource: DataSource;
|
||||
dataSourceId: number;
|
||||
pageName: string;
|
||||
loadDataSource: typeof loadDataSource;
|
||||
}
|
||||
|
||||
enum PageTypes {
|
||||
Settings = 'settings',
|
||||
Permissions = 'permissions',
|
||||
Dashboards = 'dashboards',
|
||||
}
|
||||
|
||||
export class EditDataSourcePage extends PureComponent<Props> {
|
||||
componentDidMount() {
|
||||
this.fetchDataSource();
|
||||
}
|
||||
|
||||
async fetchDataSource() {
|
||||
await this.props.loadDataSource(this.props.dataSourceId);
|
||||
}
|
||||
|
||||
isValidPage(currentPage) {
|
||||
return (Object as any).values(PageTypes).includes(currentPage);
|
||||
}
|
||||
|
||||
getCurrentPage() {
|
||||
const currentPage = this.props.pageName;
|
||||
|
||||
return this.isValidPage(currentPage) ? currentPage : PageTypes.Permissions;
|
||||
}
|
||||
|
||||
renderPage() {
|
||||
switch (this.getCurrentPage()) {
|
||||
case PageTypes.Permissions:
|
||||
return <DataSourcePermissions />;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { navModel } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PageHeader model={navModel} />
|
||||
<div className="page-container page-body">{this.renderPage()}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const pageName = getRouteParamsPage(state.location) || PageTypes.Permissions;
|
||||
const dataSourceId = getRouteParamsId(state.location);
|
||||
const dataSourceLoadingNav = getDataSourceLoadingNav(pageName);
|
||||
|
||||
return {
|
||||
navModel: getNavModel(state.navIndex, `datasource-${pageName}-${dataSourceId}`, dataSourceLoadingNav),
|
||||
dataSourceId: dataSourceId,
|
||||
dataSource: getDataSource(state.dataSources, dataSourceId),
|
||||
pageName: pageName,
|
||||
};
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
loadDataSource,
|
||||
};
|
||||
|
||||
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(EditDataSourcePage));
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DataSource, DataSourcePermission } from 'app/types';
|
||||
import { DataSource } from 'app/types';
|
||||
|
||||
export const getMockDataSources = (amount: number): DataSource[] => {
|
||||
const dataSources = [];
|
||||
@@ -43,32 +43,3 @@ export const getMockDataSource = (): DataSource => {
|
||||
user: '',
|
||||
};
|
||||
};
|
||||
|
||||
export const getMockDataSourcePermissionsUser = (): DataSourcePermission => {
|
||||
return {
|
||||
created: '2018-10-10T16:50:45+02:00',
|
||||
datasourceId: 1,
|
||||
id: 2,
|
||||
permission: 1,
|
||||
permissionName: 'Query',
|
||||
updated: '2018-10-10T16:50:45+02:00',
|
||||
userAvatarUrl: '/avatar/926aa85c6bcefa0b4deca3223f337ae1',
|
||||
userEmail: 'test@test.com',
|
||||
userId: 3,
|
||||
userLogin: 'testUser',
|
||||
};
|
||||
};
|
||||
|
||||
export const getMockDataSourcePermissionsTeam = (): DataSourcePermission => {
|
||||
return {
|
||||
created: '2018-10-10T16:57:09+02:00',
|
||||
datasourceId: 1,
|
||||
id: 6,
|
||||
permission: 1,
|
||||
permissionName: 'Query',
|
||||
team: 'A-team',
|
||||
teamAvatarUrl: '/avatar/93c0801b955cbd443a8cfa91a401d7bc',
|
||||
teamId: 1,
|
||||
updated: '2018-10-10T16:57:09+02:00',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Render should render component 1`] = `
|
||||
<div
|
||||
className="gf-form-inline cta-form"
|
||||
>
|
||||
<button
|
||||
className="cta-form__close btn btn-transparent"
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<i
|
||||
className="fa fa-close"
|
||||
/>
|
||||
</button>
|
||||
<form
|
||||
name="addPermission"
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<h5>
|
||||
Add Permission For
|
||||
</h5>
|
||||
<div
|
||||
className="gf-form-inline"
|
||||
>
|
||||
<div
|
||||
className="gf-form"
|
||||
>
|
||||
<select
|
||||
className="gf-form-input gf-size-auto"
|
||||
onChange={[Function]}
|
||||
value="Team"
|
||||
>
|
||||
<option
|
||||
key="0"
|
||||
value="Team"
|
||||
>
|
||||
Team
|
||||
</option>
|
||||
<option
|
||||
key="1"
|
||||
value="User"
|
||||
>
|
||||
User
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div
|
||||
className="gf-form"
|
||||
>
|
||||
<TeamPicker
|
||||
className="width-20"
|
||||
onSelected={[Function]}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="gf-form"
|
||||
>
|
||||
<DescriptionPicker
|
||||
className="gf-form-input--form-dropdown-right"
|
||||
disabled={false}
|
||||
onSelected={[Function]}
|
||||
optionsWithDesc={
|
||||
Array [
|
||||
Object {
|
||||
"description": "Can query data source.",
|
||||
"label": "Query",
|
||||
"value": 1,
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="gf-form"
|
||||
>
|
||||
<button
|
||||
className="btn btn-success"
|
||||
data-save-permission={true}
|
||||
disabled={true}
|
||||
type="submit"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Render should render user picker 1`] = `
|
||||
<div
|
||||
className="gf-form-inline cta-form"
|
||||
>
|
||||
<button
|
||||
className="cta-form__close btn btn-transparent"
|
||||
onClick={[MockFunction]}
|
||||
>
|
||||
<i
|
||||
className="fa fa-close"
|
||||
/>
|
||||
</button>
|
||||
<form
|
||||
name="addPermission"
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<h5>
|
||||
Add Permission For
|
||||
</h5>
|
||||
<div
|
||||
className="gf-form-inline"
|
||||
>
|
||||
<div
|
||||
className="gf-form"
|
||||
>
|
||||
<select
|
||||
className="gf-form-input gf-size-auto"
|
||||
onChange={[Function]}
|
||||
value="User"
|
||||
>
|
||||
<option
|
||||
key="0"
|
||||
value="Team"
|
||||
>
|
||||
Team
|
||||
</option>
|
||||
<option
|
||||
key="1"
|
||||
value="User"
|
||||
>
|
||||
User
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div
|
||||
className="gf-form"
|
||||
>
|
||||
<UserPicker
|
||||
className="width-20"
|
||||
onSelected={[Function]}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="gf-form"
|
||||
>
|
||||
<DescriptionPicker
|
||||
className="gf-form-input--form-dropdown-right"
|
||||
disabled={false}
|
||||
onSelected={[Function]}
|
||||
optionsWithDesc={
|
||||
Array [
|
||||
Object {
|
||||
"description": "Can query data source.",
|
||||
"label": "Query",
|
||||
"value": 1,
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="gf-form"
|
||||
>
|
||||
<button
|
||||
className="btn btn-success"
|
||||
data-save-permission={true}
|
||||
disabled={true}
|
||||
type="submit"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
`;
|
||||
@@ -1,92 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Render should render component 1`] = `
|
||||
<div>
|
||||
<div
|
||||
className="page-action-bar"
|
||||
>
|
||||
<h3
|
||||
className="page-sub-heading"
|
||||
>
|
||||
Permissions
|
||||
</h3>
|
||||
<div
|
||||
className="page-action-bar__spacer"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="empty-list-cta"
|
||||
>
|
||||
<div
|
||||
className="empty-list-cta__title"
|
||||
>
|
||||
Permissions not enabled for this data source.
|
||||
</div>
|
||||
<button
|
||||
className="empty-list-cta__button btn btn-xlarge btn-success"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Enable
|
||||
</button>
|
||||
<div
|
||||
className="empty-list-cta__pro-tip"
|
||||
>
|
||||
<i
|
||||
className="fa fa-rocket"
|
||||
/>
|
||||
ProTip:
|
||||
|
||||
Only admins will be able to query the data source after you enable permissions.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Render should render permissions enabled 1`] = `
|
||||
<div>
|
||||
<div
|
||||
className="page-action-bar"
|
||||
>
|
||||
<h3
|
||||
className="page-sub-heading"
|
||||
>
|
||||
Permissions
|
||||
</h3>
|
||||
<div
|
||||
className="page-action-bar__spacer"
|
||||
/>
|
||||
<button
|
||||
className="btn btn-success pull-right"
|
||||
disabled={false}
|
||||
key="add-permission"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<i
|
||||
className="fa fa-plus"
|
||||
/>
|
||||
Add Permission
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-danger pull-right"
|
||||
key="disable-permissions"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Disable Permissions
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<Component
|
||||
in={false}
|
||||
>
|
||||
<AddDataSourcePermissions
|
||||
onAddPermission={[Function]}
|
||||
onCancel={[Function]}
|
||||
/>
|
||||
</Component>
|
||||
<DataSourcePermissionsList
|
||||
items={Array []}
|
||||
onRemoveItem={[Function]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
-327
@@ -1,327 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Render should render component 1`] = `
|
||||
<table
|
||||
className="filter-table gf-form-group"
|
||||
>
|
||||
<tbody>
|
||||
<tr
|
||||
className="gf-form-disabled"
|
||||
>
|
||||
<td
|
||||
style={
|
||||
Object {
|
||||
"width": "1%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<i
|
||||
className="gicon gicon-shield"
|
||||
style={
|
||||
Object {
|
||||
"height": "25px",
|
||||
"width": "25px",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
<td
|
||||
style={
|
||||
Object {
|
||||
"width": "90%",
|
||||
}
|
||||
}
|
||||
>
|
||||
Admin
|
||||
<span
|
||||
className="filter-table__weak-italic"
|
||||
>
|
||||
(Role)
|
||||
</span>
|
||||
</td>
|
||||
<td />
|
||||
<td
|
||||
className="query-keyword"
|
||||
>
|
||||
Can
|
||||
</td>
|
||||
<td>
|
||||
<div
|
||||
className="gf-form"
|
||||
>
|
||||
<DescriptionPicker
|
||||
className="gf-form-input--form-dropdown-right"
|
||||
disabled={true}
|
||||
onSelected={[Function]}
|
||||
optionsWithDesc={
|
||||
Array [
|
||||
Object {
|
||||
"description": "Can query data source.",
|
||||
"label": "Query",
|
||||
"value": 1,
|
||||
},
|
||||
Object {
|
||||
"description": "",
|
||||
"label": "Admin",
|
||||
"value": 2,
|
||||
},
|
||||
]
|
||||
}
|
||||
value={2}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
className="btn btn-inverse btn-small"
|
||||
>
|
||||
<i
|
||||
className="fa fa-lock"
|
||||
/>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
|
||||
exports[`Render should render items 1`] = `
|
||||
<table
|
||||
className="filter-table gf-form-group"
|
||||
>
|
||||
<tbody>
|
||||
<tr
|
||||
className="gf-form-disabled"
|
||||
>
|
||||
<td
|
||||
style={
|
||||
Object {
|
||||
"width": "1%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<i
|
||||
className="gicon gicon-shield"
|
||||
style={
|
||||
Object {
|
||||
"height": "25px",
|
||||
"width": "25px",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
<td
|
||||
style={
|
||||
Object {
|
||||
"width": "90%",
|
||||
}
|
||||
}
|
||||
>
|
||||
Admin
|
||||
<span
|
||||
className="filter-table__weak-italic"
|
||||
>
|
||||
(Role)
|
||||
</span>
|
||||
</td>
|
||||
<td />
|
||||
<td
|
||||
className="query-keyword"
|
||||
>
|
||||
Can
|
||||
</td>
|
||||
<td>
|
||||
<div
|
||||
className="gf-form"
|
||||
>
|
||||
<DescriptionPicker
|
||||
className="gf-form-input--form-dropdown-right"
|
||||
disabled={true}
|
||||
onSelected={[Function]}
|
||||
optionsWithDesc={
|
||||
Array [
|
||||
Object {
|
||||
"description": "Can query data source.",
|
||||
"label": "Query",
|
||||
"value": 1,
|
||||
},
|
||||
Object {
|
||||
"description": "",
|
||||
"label": "Admin",
|
||||
"value": 2,
|
||||
},
|
||||
]
|
||||
}
|
||||
value={2}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
className="btn btn-inverse btn-small"
|
||||
>
|
||||
<i
|
||||
className="fa fa-lock"
|
||||
/>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
key="2-0"
|
||||
>
|
||||
<td
|
||||
style={
|
||||
Object {
|
||||
"width": "1%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<img
|
||||
className="filter-table__avatar"
|
||||
src="/avatar/926aa85c6bcefa0b4deca3223f337ae1"
|
||||
/>
|
||||
</td>
|
||||
<td
|
||||
style={
|
||||
Object {
|
||||
"width": "90%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<span
|
||||
key="name"
|
||||
>
|
||||
testUser
|
||||
|
||||
</span>
|
||||
<span
|
||||
className="filter-table__weak-italic"
|
||||
key="description"
|
||||
>
|
||||
(User)
|
||||
</span>
|
||||
</td>
|
||||
<td />
|
||||
<td
|
||||
className="query-keyword"
|
||||
>
|
||||
Can
|
||||
</td>
|
||||
<td>
|
||||
<div
|
||||
className="gf-form"
|
||||
>
|
||||
<DescriptionPicker
|
||||
className="gf-form-input--form-dropdown-right"
|
||||
disabled={true}
|
||||
onSelected={[Function]}
|
||||
optionsWithDesc={
|
||||
Array [
|
||||
Object {
|
||||
"description": "Can query data source.",
|
||||
"label": "Query",
|
||||
"value": 1,
|
||||
},
|
||||
Object {
|
||||
"description": "",
|
||||
"label": "Admin",
|
||||
"value": 2,
|
||||
},
|
||||
]
|
||||
}
|
||||
value={1}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
className="btn btn-danger btn-small"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<i
|
||||
className="fa fa-remove"
|
||||
/>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
key="6-1"
|
||||
>
|
||||
<td
|
||||
style={
|
||||
Object {
|
||||
"width": "1%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<img
|
||||
className="filter-table__avatar"
|
||||
src="/avatar/93c0801b955cbd443a8cfa91a401d7bc"
|
||||
/>
|
||||
</td>
|
||||
<td
|
||||
style={
|
||||
Object {
|
||||
"width": "90%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<span
|
||||
key="name"
|
||||
>
|
||||
A-team
|
||||
|
||||
</span>
|
||||
<span
|
||||
className="filter-table__weak-italic"
|
||||
key="description"
|
||||
>
|
||||
(Team)
|
||||
</span>
|
||||
</td>
|
||||
<td />
|
||||
<td
|
||||
className="query-keyword"
|
||||
>
|
||||
Can
|
||||
</td>
|
||||
<td>
|
||||
<div
|
||||
className="gf-form"
|
||||
>
|
||||
<DescriptionPicker
|
||||
className="gf-form-input--form-dropdown-right"
|
||||
disabled={true}
|
||||
onSelected={[Function]}
|
||||
optionsWithDesc={
|
||||
Array [
|
||||
Object {
|
||||
"description": "Can query data source.",
|
||||
"label": "Query",
|
||||
"value": 1,
|
||||
},
|
||||
Object {
|
||||
"description": "",
|
||||
"label": "Admin",
|
||||
"value": 2,
|
||||
},
|
||||
]
|
||||
}
|
||||
value={1}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
className="btn btn-danger btn-small"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<i
|
||||
className="fa fa-remove"
|
||||
/>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ThunkAction } from 'redux-thunk';
|
||||
import { DataSource, DataSourcePermissionDTO, Plugin, StoreState } from 'app/types';
|
||||
import { DataSource, Plugin, StoreState } from 'app/types';
|
||||
import { getBackendSrv } from '../../../core/services/backend_srv';
|
||||
import { LayoutMode } from '../../../core/components/LayoutSelector/LayoutSelector';
|
||||
import { updateLocation, updateNavIndex, UpdateNavIndexAction } from '../../../core/actions';
|
||||
@@ -11,7 +11,6 @@ export enum ActionTypes {
|
||||
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',
|
||||
@@ -52,11 +51,6 @@ export interface LoadDataSourceMetaAction {
|
||||
payload: Plugin;
|
||||
}
|
||||
|
||||
export interface LoadDataSourcePermissionsAction {
|
||||
type: ActionTypes.LoadDataSourcePermissions;
|
||||
payload: DataSourcePermissionDTO;
|
||||
}
|
||||
|
||||
const dataSourcesLoaded = (dataSources: DataSource[]): LoadDataSourcesAction => ({
|
||||
type: ActionTypes.LoadDataSources,
|
||||
payload: dataSources,
|
||||
@@ -77,13 +71,6 @@ const dataSourceTypesLoaded = (dataSourceTypes: Plugin[]): LoadDataSourceTypesAc
|
||||
payload: dataSourceTypes,
|
||||
});
|
||||
|
||||
const dataSourcePermissionsLoaded = (
|
||||
dataSourcePermission: DataSourcePermissionDTO
|
||||
): LoadDataSourcePermissionsAction => ({
|
||||
type: ActionTypes.LoadDataSourcePermissions,
|
||||
payload: dataSourcePermission,
|
||||
});
|
||||
|
||||
export const setDataSourcesSearchQuery = (searchQuery: string): SetDataSourcesSearchQueryAction => ({
|
||||
type: ActionTypes.SetDataSourcesSearchQuery,
|
||||
payload: searchQuery,
|
||||
@@ -108,8 +95,7 @@ export type Action =
|
||||
| SetDataSourceTypeSearchQueryAction
|
||||
| LoadDataSourceAction
|
||||
| UpdateNavIndexAction
|
||||
| LoadDataSourceMetaAction
|
||||
| LoadDataSourcePermissionsAction;
|
||||
| LoadDataSourceMetaAction;
|
||||
|
||||
type ThunkResult<R> = ThunkAction<R, StoreState, undefined, Action>;
|
||||
|
||||
@@ -159,42 +145,6 @@ 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));
|
||||
};
|
||||
}
|
||||
|
||||
export function enableDataSourcePermissions(id: number): ThunkResult<void> {
|
||||
return async dispatch => {
|
||||
await getBackendSrv().post(`/api/datasources/${id}/enable-permissions`, {});
|
||||
dispatch(loadDataSourcePermissions(id));
|
||||
};
|
||||
}
|
||||
|
||||
export function disableDataSourcePermissions(id: number): ThunkResult<void> {
|
||||
return async dispatch => {
|
||||
await getBackendSrv().post(`/api/datasources/${id}/disable-permissions`, {});
|
||||
dispatch(loadDataSourcePermissions(id));
|
||||
};
|
||||
}
|
||||
|
||||
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 => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DataSource, DataSourcePermissionDTO, DataSourcesState, Plugin } from 'app/types';
|
||||
import { DataSource, DataSourcesState, Plugin } from 'app/types';
|
||||
import { Action, ActionTypes } from './actions';
|
||||
import { LayoutModes } from '../../../core/components/LayoutSelector/LayoutSelector';
|
||||
|
||||
@@ -11,7 +11,6 @@ const initialState: DataSourcesState = {
|
||||
dataSourceTypes: [] as Plugin[],
|
||||
dataSourceTypeSearchQuery: '',
|
||||
dataSourceMeta: {} as Plugin,
|
||||
dataSourcePermission: {} as DataSourcePermissionDTO,
|
||||
hasFetched: false,
|
||||
};
|
||||
|
||||
@@ -37,9 +36,6 @@ export const dataSourcesReducer = (state = initialState, action: Action): DataSo
|
||||
|
||||
case ActionTypes.LoadDataSourceMeta:
|
||||
return { ...state, dataSourceMeta: action.payload };
|
||||
|
||||
case ActionTypes.LoadDataSourcePermissions:
|
||||
return { ...state, dataSourcePermission: action.payload };
|
||||
}
|
||||
|
||||
return state;
|
||||
|
||||
@@ -13,7 +13,6 @@ import FolderPermissions from 'app/features/folders/FolderPermissions';
|
||||
import DataSourcesListPage from 'app/features/datasources/DataSourcesListPage';
|
||||
import NewDataSourcePage from '../features/datasources/NewDataSourcePage';
|
||||
import UsersListPage from 'app/features/users/UsersListPage';
|
||||
import EditDataSourcePage from 'app/features/datasources/EditDataSourcePage';
|
||||
|
||||
/** @ngInject */
|
||||
export function setupAngularRoutes($routeProvider, $locationProvider) {
|
||||
@@ -83,12 +82,6 @@ export function setupAngularRoutes($routeProvider, $locationProvider) {
|
||||
controller: 'DataSourceDashboardsCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
})
|
||||
.when('/datasources/edit/:id/:page?', {
|
||||
template: '<react-container />',
|
||||
resolve: {
|
||||
component: () => EditDataSourcePage,
|
||||
},
|
||||
})
|
||||
.when('/datasources/new', {
|
||||
template: '<react-container />',
|
||||
resolve: {
|
||||
|
||||
@@ -11,7 +11,7 @@ import pluginReducers from 'app/features/plugins/state/reducers';
|
||||
import dataSourcesReducers from 'app/features/datasources/state/reducers';
|
||||
import usersReducers from 'app/features/users/state/reducers';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
const rootReducers = {
|
||||
...sharedReducers,
|
||||
...alertingReducers,
|
||||
...teamsReducers,
|
||||
@@ -21,13 +21,19 @@ const rootReducer = combineReducers({
|
||||
...pluginReducers,
|
||||
...dataSourcesReducers,
|
||||
...usersReducers,
|
||||
});
|
||||
};
|
||||
|
||||
export let store;
|
||||
|
||||
export function addRootReducer(reducers) {
|
||||
Object.assign(rootReducers, ...reducers);
|
||||
}
|
||||
|
||||
export function configureStore() {
|
||||
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||
|
||||
const rootReducer = combineReducers(rootReducers);
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// DEV builds we had the logger middleware
|
||||
store = createStore(rootReducer, {}, composeEnhancers(applyMiddleware(thunk, createLogger())));
|
||||
|
||||
@@ -1,28 +1,6 @@
|
||||
import { LayoutMode } from '../core/components/LayoutSelector/LayoutSelector';
|
||||
import { Plugin } from './plugins';
|
||||
|
||||
export interface DataSourcePermission {
|
||||
id: number;
|
||||
datasourceId: number;
|
||||
permission: number;
|
||||
permissionName: string;
|
||||
created: string;
|
||||
updated: string;
|
||||
userId?: number;
|
||||
userLogin?: string;
|
||||
userEmail?: string;
|
||||
userAvatarUrl?: string;
|
||||
teamId?: number;
|
||||
teamAvatarUrl?: string;
|
||||
team?: string;
|
||||
}
|
||||
|
||||
export interface DataSourcePermissionDTO {
|
||||
datasourceId: number;
|
||||
enabled: boolean;
|
||||
permissions: DataSourcePermission[];
|
||||
}
|
||||
|
||||
export interface DataSource {
|
||||
id: number;
|
||||
orgId: number;
|
||||
@@ -49,6 +27,5 @@ export interface DataSourcesState {
|
||||
dataSourceTypes: Plugin[];
|
||||
dataSource: DataSource;
|
||||
dataSourceMeta: Plugin;
|
||||
dataSourcePermission: DataSourcePermissionDTO;
|
||||
hasFetched: boolean;
|
||||
}
|
||||
|
||||
@@ -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, DataSourcePermissionDTO, DataSourcePermission, DataSourcesState } from './datasources';
|
||||
import { DataSource, DataSourcesState } from './datasources';
|
||||
import { PluginMeta, Plugin, PluginsState } from './plugins';
|
||||
|
||||
export {
|
||||
@@ -41,8 +41,6 @@ export {
|
||||
Plugin,
|
||||
PluginsState,
|
||||
DataSourcesState,
|
||||
DataSourcePermissionDTO,
|
||||
DataSourcePermission,
|
||||
Invitee,
|
||||
OrgUser,
|
||||
User,
|
||||
|
||||
Reference in New Issue
Block a user