1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import { Dispatch } from 'redux';
6 import { reset, startSubmit, stopSubmit, FormErrors } from 'redux-form';
7 import { bindDataExplorerActions } from "~/store/data-explorer/data-explorer-action";
8 import { dialogActions } from '~/store/dialog/dialog-actions';
9 import { Person } from '~/views-components/sharing-dialog/people-select';
10 import { RootState } from '~/store/store';
11 import { ServiceRepository } from '~/services/services';
12 import { getResource } from '~/store/resources/resources';
13 import { GroupResource } from '~/models/group';
14 import { getCommonResourceServiceError, CommonResourceServiceError } from '~/services/common-service/common-resource-service';
15 import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
16 import { PermissionLevel } from '~/models/permission';
17 import { PermissionService } from '~/services/permission-service/permission-service';
19 export const GROUPS_PANEL_ID = "groupsPanel";
20 export const CREATE_GROUP_DIALOG = "createGroupDialog";
21 export const CREATE_GROUP_FORM = "createGroupForm";
22 export const CREATE_GROUP_NAME_FIELD_NAME = 'name';
23 export const CREATE_GROUP_USERS_FIELD_NAME = 'users';
24 export const GROUP_ATTRIBUTES_DIALOG = 'groupAttributesDialog';
25 export const GROUP_REMOVE_DIALOG = 'groupRemoveDialog';
27 export const GroupsPanelActions = bindDataExplorerActions(GROUPS_PANEL_ID);
29 export const loadGroupsPanel = () => GroupsPanelActions.REQUEST_ITEMS();
31 export const openCreateGroupDialog = () =>
32 (dispatch: Dispatch) => {
33 dispatch(dialogActions.OPEN_DIALOG({ id: CREATE_GROUP_DIALOG, data: {} }));
34 dispatch(reset(CREATE_GROUP_FORM));
37 export const openGroupAttributes = (uuid: string) =>
38 (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
39 const { resources } = getState();
40 const data = getResource<GroupResource>(uuid)(resources);
41 dispatch(dialogActions.OPEN_DIALOG({ id: GROUP_ATTRIBUTES_DIALOG, data }));
44 export const removeGroup = (uuid: string) =>
45 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
46 dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removing ...' }));
47 await services.groupsService.delete(uuid);
48 dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removed.', hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
49 dispatch<any>(loadGroupsPanel());
52 export const openRemoveGroupDialog = (uuid: string) =>
53 (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
54 dispatch(dialogActions.OPEN_DIALOG({
55 id: GROUP_REMOVE_DIALOG,
57 title: 'Remove group',
58 text: 'Are you sure you want to remove this group?',
59 confirmButtonLabel: 'Remove',
65 export interface CreateGroupFormData {
66 [CREATE_GROUP_NAME_FIELD_NAME]: string;
67 [CREATE_GROUP_USERS_FIELD_NAME]?: Person[];
70 export const createGroup = ({ name, users = [] }: CreateGroupFormData) =>
71 async (dispatch: Dispatch, _: {}, { groupsService, permissionService }: ServiceRepository) => {
73 dispatch(startSubmit(CREATE_GROUP_FORM));
77 const newGroup = await groupsService.create({ name });
79 for (const user of users) {
81 await createPermissionLink({
82 head: { ...newGroup },
84 permissionLevel: PermissionLevel.CAN_READ,
89 await createPermissionLink({
91 tail: { ...newGroup },
92 permissionLevel: PermissionLevel.CAN_READ,
99 dispatch(dialogActions.CLOSE_DIALOG({ id: CREATE_GROUP_DIALOG }));
100 dispatch(reset(CREATE_GROUP_FORM));
101 dispatch(loadGroupsPanel());
102 dispatch(snackbarActions.OPEN_SNACKBAR({
103 message: `${newGroup.name} group has been created`,
104 kind: SnackbarKind.SUCCESS
111 const error = getCommonResourceServiceError(e);
112 if (error === CommonResourceServiceError.UNIQUE_VIOLATION) {
113 dispatch(stopSubmit(CREATE_GROUP_FORM, { name: 'Group with the same name already exists.' } as FormErrors));
122 interface CreatePermissionLinkArgs {
123 head: { uuid: string, name: string };
124 tail: { uuid: string, name: string };
125 permissionLevel: PermissionLevel;
127 permissionService: PermissionService;
130 const createPermissionLink = async ({ head, tail, permissionLevel, dispatch, permissionService }: CreatePermissionLinkArgs) => {
134 await permissionService.create({
137 name: permissionLevel,
142 dispatch(snackbarActions.OPEN_SNACKBAR({
143 message: `Could not add ${tail.name} -> ${head.name} relation`,
144 kind: SnackbarKind.ERROR,