dashfolders: autosave permissions on change (remove update button)

This commit is contained in:
Daniel Lee 2018-01-26 14:02:22 +01:00
parent 08c78ab8b7
commit ccd86873e7
5 changed files with 126 additions and 59 deletions

View File

@ -34,14 +34,17 @@ export interface IProps {
class Permissions extends Component<IProps, any> { class Permissions extends Component<IProps, any> {
constructor(props) { constructor(props) {
super(props); super(props);
const { dashboardId, permissions, isFolder } = this.props; const { dashboardId, isFolder } = this.props;
this.permissionChanged = this.permissionChanged.bind(this); this.permissionChanged = this.permissionChanged.bind(this);
this.typeChanged = this.typeChanged.bind(this); this.typeChanged = this.typeChanged.bind(this);
this.removeItem = this.removeItem.bind(this); this.removeItem = this.removeItem.bind(this);
this.update = this.update.bind(this);
this.userPicked = this.userPicked.bind(this); this.userPicked = this.userPicked.bind(this);
this.teamPicked = this.teamPicked.bind(this); this.teamPicked = this.teamPicked.bind(this);
permissions.load(dashboardId, isFolder); this.loadStore(dashboardId, isFolder);
}
loadStore(dashboardId, isFolder) {
return this.props.permissions.load(dashboardId, isFolder);
} }
permissionChanged(index: number, permission: number, permissionName: string) { permissionChanged(index: number, permission: number, permissionName: string) {
@ -54,11 +57,6 @@ class Permissions extends Component<IProps, any> {
permissions.removeStoreItem(index); permissions.removeStoreItem(index);
} }
update() {
const { permissions, dashboardId } = this.props;
permissions.update(dashboardId);
}
resetNewType() { resetNewType() {
const { permissions } = this.props; const { permissions } = this.props;
permissions.resetNewType(); permissions.resetNewType();
@ -142,11 +140,6 @@ class Permissions extends Component<IProps, any> {
</div> </div>
) : null} ) : null}
</div> </div>
<div className="gf-form-button-row">
<button type="button" className="btn btn-danger" onClick={this.update} disabled={!permissions.canUpdate}>
Update Permissions
</button>
</div>
<div className="empty-list-cta m-t-3"> <div className="empty-list-cta m-t-3">
<div className="grafana-info-box"> <div className="grafana-info-box">
<h5>What are Permissions?</h5> <h5>What are Permissions?</h5>

View File

@ -69,11 +69,6 @@ export function setupAngularRoutes($routeProvider, $locationProvider) {
controller: 'CreateFolderCtrl', controller: 'CreateFolderCtrl',
controllerAs: 'ctrl', controllerAs: 'ctrl',
}) })
// .when('/dashboards/folder/:folderId/:slug/permissions', {
// templateUrl: 'public/app/features/dashboard/partials/folder_permissions.html',
// controller: 'FolderPermissionsCtrl',
// controllerAs: 'ctrl',
// })
.when('/dashboards/folder/:folderId/:slug/permissions', { .when('/dashboards/folder/:folderId/:slug/permissions', {
template: '<react-container />', template: '<react-container />',
resolve: { resolve: {

View File

@ -0,0 +1,76 @@
import { PermissionsStore } from './PermissionsStore';
import { backendSrv } from 'test/mocks/common';
describe('PermissionsStore', () => {
let store;
beforeEach(() => {
backendSrv.get.mockReturnValue(
Promise.resolve([
{ id: 2, dashboardId: 1, role: 'Viewer', permission: 1, permissionName: 'View' },
{ id: 3, dashboardId: 1, role: 'Editor', permission: 1, permissionName: 'Edit' },
{
id: 4,
dashboardId: 1,
userId: 2,
userLogin: 'danlimerick',
userEmail: 'dan.limerick@gmail.com',
permission: 4,
permissionName: 'Admin',
},
])
);
backendSrv.post = jest.fn();
store = PermissionsStore.create(
{
fetching: false,
canUpdate: false,
items: [],
},
{
backendSrv: backendSrv,
}
);
return store.load(1, true);
});
it('should save update on permission change', () => {
expect(store.items[0].permission).toBe(1);
expect(store.items[0].permissionName).toBe('View');
store.updatePermissionOnIndex(0, 2, 'Edit');
expect(store.items[0].permission).toBe(2);
expect(store.items[0].permissionName).toBe('Edit');
expect(backendSrv.post.mock.calls.length).toBe(1);
expect(backendSrv.post.mock.calls[0][0]).toBe('/api/dashboards/id/1/acl');
});
it('should save newly added permissions automatically', () => {
expect(store.items.length).toBe(3);
const newItem = {
userId: 10,
userLogin: 'tester1',
permission: 1,
};
store.addStoreItem(newItem);
expect(store.items.length).toBe(4);
expect(backendSrv.post.mock.calls.length).toBe(1);
expect(backendSrv.post.mock.calls[0][0]).toBe('/api/dashboards/id/1/acl');
});
it('should save removed permissions automatically', () => {
expect(store.items.length).toBe(3);
store.removeStoreItem(2);
expect(store.items.length).toBe(2);
expect(backendSrv.post.mock.calls.length).toBe(1);
expect(backendSrv.post.mock.calls[0][0]).toBe('/api/dashboards/id/1/acl');
});
});

View File

@ -19,7 +19,6 @@ export const PermissionsStore = types
fetching: types.boolean, fetching: types.boolean,
isFolder: types.maybe(types.boolean), isFolder: types.maybe(types.boolean),
dashboardId: types.maybe(types.number), dashboardId: types.maybe(types.number),
canUpdate: types.boolean,
items: types.optional(types.array(PermissionsStoreItem), []), items: types.optional(types.array(PermissionsStoreItem), []),
error: types.maybe(types.string), error: types.maybe(types.string),
originalItems: types.optional(types.array(PermissionsStoreItem), []), originalItems: types.optional(types.array(PermissionsStoreItem), []),
@ -51,62 +50,67 @@ export const PermissionsStore = types
self.originalItems = items; self.originalItems = items;
self.fetching = false; self.fetching = false;
}), }),
addStoreItem: item => { addStoreItem: flow(function* addStoreItem(item) {
self.error = null; self.error = null;
if (!self.isValid(item)) { if (!self.isValid(item)) {
return; return undefined;
} }
self.items.push(prepareItem(item, self.dashboardId, self.isFolder)); self.items.push(prepareItem(item, self.dashboardId, self.isFolder));
self.canUpdate = true; updateItems(self);
}, }),
removeStoreItem: idx => { removeStoreItem: flow(function* removeStoreItem(idx: number) {
self.error = null; self.error = null;
self.items.splice(idx, 1); self.items.splice(idx, 1);
self.canUpdate = true; updateItems(self);
}, }),
updatePermissionOnIndex(idx: number, permission: number, permissionName: string) { updatePermissionOnIndex: flow(function* updatePermissionOnIndex(
idx: number,
permission: number,
permissionName: string
) {
self.error = null; self.error = null;
self.items[idx].updatePermission(permission, permissionName); self.items[idx].updatePermission(permission, permissionName);
self.canUpdate = true; updateItems(self);
}, }),
setNewType(newType: string) { setNewType(newType: string) {
self.newType = newType; self.newType = newType;
}, },
resetNewType() { resetNewType() {
self.newType = defaultNewType; self.newType = defaultNewType;
}, },
update: flow(function* update(dashboardId: number) {
self.error = null;
const backendSrv = getEnv(self).backendSrv;
const updated = [];
for (let item of self.items) {
if (item.inherited) {
continue;
}
updated.push({
id: item.id,
userId: item.userId,
teamId: item.teamId,
role: item.role,
permission: item.permission,
});
}
let res;
try {
res = backendSrv.post(`/api/dashboards/id/${dashboardId}/acl`, {
items: updated,
});
} catch (error) {
console.error(error);
}
self.canUpdate = false;
return res;
}),
})); }));
const updateItems = self => {
self.error = null;
const backendSrv = getEnv(self).backendSrv;
const updated = [];
for (let item of self.items) {
if (item.inherited) {
continue;
}
updated.push({
id: item.id,
userId: item.userId,
teamId: item.teamId,
role: item.role,
permission: item.permission,
});
}
let res;
try {
res = backendSrv.post(`/api/dashboards/id/${self.dashboardId}/acl`, {
items: updated,
});
} catch (error) {
console.error(error);
}
return res;
};
const prepareServerResponse = (response, dashboardId: number, isFolder: boolean) => { const prepareServerResponse = (response, dashboardId: number, isFolder: boolean) => {
return response.map(item => { return response.map(item => {
return prepareItem(item, dashboardId, isFolder); return prepareItem(item, dashboardId, isFolder);

View File

@ -20,7 +20,6 @@ export const RootStore = types.model({
}), }),
permissions: types.optional(PermissionsStore, { permissions: types.optional(PermissionsStore, {
fetching: false, fetching: false,
canUpdate: false,
items: [], items: [],
}), }),
view: types.optional(ViewStore, { view: types.optional(ViewStore, {